mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-24 21:54:34 -04:00
Merge Linus's 5.4-rc1-prerelease branch into android-mainline
This merges Linus's tree as of commit b41dae061b ("Merge tag
'xfs-5.4-merge-7' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux")
into android-mainline.
This "early" merge makes it easier to test and handle merge conflicts
instead of having to wait until the "end" of the merge window and handle
all 10000+ commits at once.
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
Change-Id: I6bebf55e5e2353f814e3c87f5033607b1ae5d812
This commit is contained in:
commit
bfa0399bc8
7251 changed files with 334804 additions and 222478 deletions
|
|
@ -64,6 +64,7 @@ obj-$(CONFIG_CRASH_CORE) += crash_core.o
|
|||
obj-$(CONFIG_KEXEC_CORE) += kexec_core.o
|
||||
obj-$(CONFIG_KEXEC) += kexec.o
|
||||
obj-$(CONFIG_KEXEC_FILE) += kexec_file.o
|
||||
obj-$(CONFIG_KEXEC_ELF) += kexec_elf.o
|
||||
obj-$(CONFIG_BACKTRACE_SELF_TEST) += backtracetest.o
|
||||
obj-$(CONFIG_COMPAT) += compat.o
|
||||
obj-$(CONFIG_CGROUPS) += cgroup/
|
||||
|
|
|
|||
|
|
@ -22,3 +22,6 @@ obj-$(CONFIG_CGROUP_BPF) += cgroup.o
|
|||
ifeq ($(CONFIG_INET),y)
|
||||
obj-$(CONFIG_BPF_SYSCALL) += reuseport_array.o
|
||||
endif
|
||||
ifeq ($(CONFIG_SYSFS),y)
|
||||
obj-$(CONFIG_DEBUG_INFO_BTF) += sysfs_btf.o
|
||||
endif
|
||||
|
|
|
|||
|
|
@ -195,8 +195,8 @@
|
|||
i < btf_type_vlen(struct_type); \
|
||||
i++, member++)
|
||||
|
||||
static DEFINE_IDR(btf_idr);
|
||||
static DEFINE_SPINLOCK(btf_idr_lock);
|
||||
DEFINE_IDR(btf_idr);
|
||||
DEFINE_SPINLOCK(btf_idr_lock);
|
||||
|
||||
struct btf {
|
||||
void *data;
|
||||
|
|
@ -3376,6 +3376,15 @@ void btf_type_seq_show(const struct btf *btf, u32 type_id, void *obj,
|
|||
btf_type_ops(t)->seq_show(btf, t, type_id, obj, 0, m);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
static void bpf_btf_show_fdinfo(struct seq_file *m, struct file *filp)
|
||||
{
|
||||
const struct btf *btf = filp->private_data;
|
||||
|
||||
seq_printf(m, "btf_id:\t%u\n", btf->id);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int btf_release(struct inode *inode, struct file *filp)
|
||||
{
|
||||
btf_put(filp->private_data);
|
||||
|
|
@ -3383,6 +3392,9 @@ static int btf_release(struct inode *inode, struct file *filp)
|
|||
}
|
||||
|
||||
const struct file_operations btf_fops = {
|
||||
#ifdef CONFIG_PROC_FS
|
||||
.show_fdinfo = bpf_btf_show_fdinfo,
|
||||
#endif
|
||||
.release = btf_release,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -964,7 +964,6 @@ static int sockopt_alloc_buf(struct bpf_sockopt_kern *ctx, int max_optlen)
|
|||
return -ENOMEM;
|
||||
|
||||
ctx->optval_end = ctx->optval + max_optlen;
|
||||
ctx->optlen = max_optlen;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -984,7 +983,7 @@ int __cgroup_bpf_run_filter_setsockopt(struct sock *sk, int *level,
|
|||
.level = *level,
|
||||
.optname = *optname,
|
||||
};
|
||||
int ret;
|
||||
int ret, max_optlen;
|
||||
|
||||
/* Opportunistic check to see whether we have any BPF program
|
||||
* attached to the hook so we don't waste time allocating
|
||||
|
|
@ -994,10 +993,18 @@ int __cgroup_bpf_run_filter_setsockopt(struct sock *sk, int *level,
|
|||
__cgroup_bpf_prog_array_is_empty(cgrp, BPF_CGROUP_SETSOCKOPT))
|
||||
return 0;
|
||||
|
||||
ret = sockopt_alloc_buf(&ctx, *optlen);
|
||||
/* Allocate a bit more than the initial user buffer for
|
||||
* BPF program. The canonical use case is overriding
|
||||
* TCP_CONGESTION(nv) to TCP_CONGESTION(cubic).
|
||||
*/
|
||||
max_optlen = max_t(int, 16, *optlen);
|
||||
|
||||
ret = sockopt_alloc_buf(&ctx, max_optlen);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ctx.optlen = *optlen;
|
||||
|
||||
if (copy_from_user(ctx.optval, optval, *optlen) != 0) {
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
|
|
@ -1016,7 +1023,7 @@ int __cgroup_bpf_run_filter_setsockopt(struct sock *sk, int *level,
|
|||
if (ctx.optlen == -1) {
|
||||
/* optlen set to -1, bypass kernel */
|
||||
ret = 1;
|
||||
} else if (ctx.optlen > *optlen || ctx.optlen < -1) {
|
||||
} else if (ctx.optlen > max_optlen || ctx.optlen < -1) {
|
||||
/* optlen is out of bounds */
|
||||
ret = -EFAULT;
|
||||
} else {
|
||||
|
|
@ -1063,6 +1070,8 @@ int __cgroup_bpf_run_filter_getsockopt(struct sock *sk, int level,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
ctx.optlen = max_optlen;
|
||||
|
||||
if (!retval) {
|
||||
/* If kernel getsockopt finished successfully,
|
||||
* copy whatever was returned to the user back
|
||||
|
|
@ -1325,6 +1334,7 @@ static u32 sysctl_convert_ctx_access(enum bpf_access_type type,
|
|||
struct bpf_prog *prog, u32 *target_size)
|
||||
{
|
||||
struct bpf_insn *insn = insn_buf;
|
||||
u32 read_size;
|
||||
|
||||
switch (si->off) {
|
||||
case offsetof(struct bpf_sysctl, write):
|
||||
|
|
@ -1356,7 +1366,9 @@ static u32 sysctl_convert_ctx_access(enum bpf_access_type type,
|
|||
treg, si->dst_reg,
|
||||
offsetof(struct bpf_sysctl_kern, ppos));
|
||||
*insn++ = BPF_STX_MEM(
|
||||
BPF_SIZEOF(u32), treg, si->src_reg, 0);
|
||||
BPF_SIZEOF(u32), treg, si->src_reg,
|
||||
bpf_ctx_narrow_access_offset(
|
||||
0, sizeof(u32), sizeof(loff_t)));
|
||||
*insn++ = BPF_LDX_MEM(
|
||||
BPF_DW, treg, si->dst_reg,
|
||||
offsetof(struct bpf_sysctl_kern, tmp_reg));
|
||||
|
|
@ -1365,8 +1377,11 @@ static u32 sysctl_convert_ctx_access(enum bpf_access_type type,
|
|||
BPF_FIELD_SIZEOF(struct bpf_sysctl_kern, ppos),
|
||||
si->dst_reg, si->src_reg,
|
||||
offsetof(struct bpf_sysctl_kern, ppos));
|
||||
read_size = bpf_size_to_bytes(BPF_SIZE(si->code));
|
||||
*insn++ = BPF_LDX_MEM(
|
||||
BPF_SIZE(si->code), si->dst_reg, si->dst_reg, 0);
|
||||
BPF_SIZE(si->code), si->dst_reg, si->dst_reg,
|
||||
bpf_ctx_narrow_access_offset(
|
||||
0, read_size, sizeof(loff_t)));
|
||||
}
|
||||
*target_size = sizeof(u32);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -37,6 +37,12 @@
|
|||
* notifier hook walks the map we know that new dev references can not be
|
||||
* added by the user because core infrastructure ensures dev_get_by_index()
|
||||
* calls will fail at this point.
|
||||
*
|
||||
* The devmap_hash type is a map type which interprets keys as ifindexes and
|
||||
* indexes these using a hashmap. This allows maps that use ifindex as key to be
|
||||
* densely packed instead of having holes in the lookup array for unused
|
||||
* ifindexes. The setup and packet enqueue/send code is shared between the two
|
||||
* types of devmap; only the lookup and insertion is different.
|
||||
*/
|
||||
#include <linux/bpf.h>
|
||||
#include <net/xdp.h>
|
||||
|
|
@ -59,10 +65,11 @@ struct xdp_bulk_queue {
|
|||
|
||||
struct bpf_dtab_netdev {
|
||||
struct net_device *dev; /* must be first member, due to tracepoint */
|
||||
struct hlist_node index_hlist;
|
||||
struct bpf_dtab *dtab;
|
||||
unsigned int bit;
|
||||
struct xdp_bulk_queue __percpu *bulkq;
|
||||
struct rcu_head rcu;
|
||||
unsigned int idx; /* keep track of map index for tracepoint */
|
||||
};
|
||||
|
||||
struct bpf_dtab {
|
||||
|
|
@ -70,33 +77,45 @@ struct bpf_dtab {
|
|||
struct bpf_dtab_netdev **netdev_map;
|
||||
struct list_head __percpu *flush_list;
|
||||
struct list_head list;
|
||||
|
||||
/* these are only used for DEVMAP_HASH type maps */
|
||||
struct hlist_head *dev_index_head;
|
||||
spinlock_t index_lock;
|
||||
unsigned int items;
|
||||
u32 n_buckets;
|
||||
};
|
||||
|
||||
static DEFINE_SPINLOCK(dev_map_lock);
|
||||
static LIST_HEAD(dev_map_list);
|
||||
|
||||
static struct bpf_map *dev_map_alloc(union bpf_attr *attr)
|
||||
static struct hlist_head *dev_map_create_hash(unsigned int entries)
|
||||
{
|
||||
int i;
|
||||
struct hlist_head *hash;
|
||||
|
||||
hash = kmalloc_array(entries, sizeof(*hash), GFP_KERNEL);
|
||||
if (hash != NULL)
|
||||
for (i = 0; i < entries; i++)
|
||||
INIT_HLIST_HEAD(&hash[i]);
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
static int dev_map_init_map(struct bpf_dtab *dtab, union bpf_attr *attr)
|
||||
{
|
||||
struct bpf_dtab *dtab;
|
||||
int err, cpu;
|
||||
u64 cost;
|
||||
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
return ERR_PTR(-EPERM);
|
||||
|
||||
/* check sanity of attributes */
|
||||
if (attr->max_entries == 0 || attr->key_size != 4 ||
|
||||
attr->value_size != 4 || attr->map_flags & ~DEV_CREATE_FLAG_MASK)
|
||||
return ERR_PTR(-EINVAL);
|
||||
return -EINVAL;
|
||||
|
||||
/* Lookup returns a pointer straight to dev->ifindex, so make sure the
|
||||
* verifier prevents writes from the BPF side
|
||||
*/
|
||||
attr->map_flags |= BPF_F_RDONLY_PROG;
|
||||
|
||||
dtab = kzalloc(sizeof(*dtab), GFP_USER);
|
||||
if (!dtab)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
bpf_map_init_from_attr(&dtab->map, attr);
|
||||
|
||||
|
|
@ -104,12 +123,18 @@ static struct bpf_map *dev_map_alloc(union bpf_attr *attr)
|
|||
cost = (u64) dtab->map.max_entries * sizeof(struct bpf_dtab_netdev *);
|
||||
cost += sizeof(struct list_head) * num_possible_cpus();
|
||||
|
||||
if (attr->map_type == BPF_MAP_TYPE_DEVMAP_HASH) {
|
||||
dtab->n_buckets = roundup_pow_of_two(dtab->map.max_entries);
|
||||
|
||||
if (!dtab->n_buckets) /* Overflow check */
|
||||
return -EINVAL;
|
||||
cost += sizeof(struct hlist_head) * dtab->n_buckets;
|
||||
}
|
||||
|
||||
/* if map size is larger than memlock limit, reject it */
|
||||
err = bpf_map_charge_init(&dtab->map.memory, cost);
|
||||
if (err)
|
||||
goto free_dtab;
|
||||
|
||||
err = -ENOMEM;
|
||||
return -EINVAL;
|
||||
|
||||
dtab->flush_list = alloc_percpu(struct list_head);
|
||||
if (!dtab->flush_list)
|
||||
|
|
@ -124,19 +149,48 @@ static struct bpf_map *dev_map_alloc(union bpf_attr *attr)
|
|||
if (!dtab->netdev_map)
|
||||
goto free_percpu;
|
||||
|
||||
if (attr->map_type == BPF_MAP_TYPE_DEVMAP_HASH) {
|
||||
dtab->dev_index_head = dev_map_create_hash(dtab->n_buckets);
|
||||
if (!dtab->dev_index_head)
|
||||
goto free_map_area;
|
||||
|
||||
spin_lock_init(&dtab->index_lock);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
free_map_area:
|
||||
bpf_map_area_free(dtab->netdev_map);
|
||||
free_percpu:
|
||||
free_percpu(dtab->flush_list);
|
||||
free_charge:
|
||||
bpf_map_charge_finish(&dtab->map.memory);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static struct bpf_map *dev_map_alloc(union bpf_attr *attr)
|
||||
{
|
||||
struct bpf_dtab *dtab;
|
||||
int err;
|
||||
|
||||
if (!capable(CAP_NET_ADMIN))
|
||||
return ERR_PTR(-EPERM);
|
||||
|
||||
dtab = kzalloc(sizeof(*dtab), GFP_USER);
|
||||
if (!dtab)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
err = dev_map_init_map(dtab, attr);
|
||||
if (err) {
|
||||
kfree(dtab);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
spin_lock(&dev_map_lock);
|
||||
list_add_tail_rcu(&dtab->list, &dev_map_list);
|
||||
spin_unlock(&dev_map_lock);
|
||||
|
||||
return &dtab->map;
|
||||
|
||||
free_percpu:
|
||||
free_percpu(dtab->flush_list);
|
||||
free_charge:
|
||||
bpf_map_charge_finish(&dtab->map.memory);
|
||||
free_dtab:
|
||||
kfree(dtab);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static void dev_map_free(struct bpf_map *map)
|
||||
|
|
@ -188,6 +242,7 @@ static void dev_map_free(struct bpf_map *map)
|
|||
|
||||
free_percpu(dtab->flush_list);
|
||||
bpf_map_area_free(dtab->netdev_map);
|
||||
kfree(dtab->dev_index_head);
|
||||
kfree(dtab);
|
||||
}
|
||||
|
||||
|
|
@ -208,6 +263,70 @@ static int dev_map_get_next_key(struct bpf_map *map, void *key, void *next_key)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline struct hlist_head *dev_map_index_hash(struct bpf_dtab *dtab,
|
||||
int idx)
|
||||
{
|
||||
return &dtab->dev_index_head[idx & (dtab->n_buckets - 1)];
|
||||
}
|
||||
|
||||
struct bpf_dtab_netdev *__dev_map_hash_lookup_elem(struct bpf_map *map, u32 key)
|
||||
{
|
||||
struct bpf_dtab *dtab = container_of(map, struct bpf_dtab, map);
|
||||
struct hlist_head *head = dev_map_index_hash(dtab, key);
|
||||
struct bpf_dtab_netdev *dev;
|
||||
|
||||
hlist_for_each_entry_rcu(dev, head, index_hlist)
|
||||
if (dev->idx == key)
|
||||
return dev;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int dev_map_hash_get_next_key(struct bpf_map *map, void *key,
|
||||
void *next_key)
|
||||
{
|
||||
struct bpf_dtab *dtab = container_of(map, struct bpf_dtab, map);
|
||||
u32 idx, *next = next_key;
|
||||
struct bpf_dtab_netdev *dev, *next_dev;
|
||||
struct hlist_head *head;
|
||||
int i = 0;
|
||||
|
||||
if (!key)
|
||||
goto find_first;
|
||||
|
||||
idx = *(u32 *)key;
|
||||
|
||||
dev = __dev_map_hash_lookup_elem(map, idx);
|
||||
if (!dev)
|
||||
goto find_first;
|
||||
|
||||
next_dev = hlist_entry_safe(rcu_dereference_raw(hlist_next_rcu(&dev->index_hlist)),
|
||||
struct bpf_dtab_netdev, index_hlist);
|
||||
|
||||
if (next_dev) {
|
||||
*next = next_dev->idx;
|
||||
return 0;
|
||||
}
|
||||
|
||||
i = idx & (dtab->n_buckets - 1);
|
||||
i++;
|
||||
|
||||
find_first:
|
||||
for (; i < dtab->n_buckets; i++) {
|
||||
head = dev_map_index_hash(dtab, i);
|
||||
|
||||
next_dev = hlist_entry_safe(rcu_dereference_raw(hlist_first_rcu(head)),
|
||||
struct bpf_dtab_netdev,
|
||||
index_hlist);
|
||||
if (next_dev) {
|
||||
*next = next_dev->idx;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static int bq_xmit_all(struct xdp_bulk_queue *bq, u32 flags,
|
||||
bool in_napi_ctx)
|
||||
{
|
||||
|
|
@ -235,7 +354,7 @@ static int bq_xmit_all(struct xdp_bulk_queue *bq, u32 flags,
|
|||
out:
|
||||
bq->count = 0;
|
||||
|
||||
trace_xdp_devmap_xmit(&obj->dtab->map, obj->bit,
|
||||
trace_xdp_devmap_xmit(&obj->dtab->map, obj->idx,
|
||||
sent, drops, bq->dev_rx, dev, err);
|
||||
bq->dev_rx = NULL;
|
||||
__list_del_clearprev(&bq->flush_node);
|
||||
|
|
@ -363,6 +482,15 @@ static void *dev_map_lookup_elem(struct bpf_map *map, void *key)
|
|||
return dev ? &dev->ifindex : NULL;
|
||||
}
|
||||
|
||||
static void *dev_map_hash_lookup_elem(struct bpf_map *map, void *key)
|
||||
{
|
||||
struct bpf_dtab_netdev *obj = __dev_map_hash_lookup_elem(map,
|
||||
*(u32 *)key);
|
||||
struct net_device *dev = obj ? obj->dev : NULL;
|
||||
|
||||
return dev ? &dev->ifindex : NULL;
|
||||
}
|
||||
|
||||
static void dev_map_flush_old(struct bpf_dtab_netdev *dev)
|
||||
{
|
||||
if (dev->dev->netdev_ops->ndo_xdp_xmit) {
|
||||
|
|
@ -412,17 +540,74 @@ static int dev_map_delete_elem(struct bpf_map *map, void *key)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int dev_map_update_elem(struct bpf_map *map, void *key, void *value,
|
||||
u64 map_flags)
|
||||
static int dev_map_hash_delete_elem(struct bpf_map *map, void *key)
|
||||
{
|
||||
struct bpf_dtab *dtab = container_of(map, struct bpf_dtab, map);
|
||||
struct net *net = current->nsproxy->net_ns;
|
||||
struct bpf_dtab_netdev *old_dev;
|
||||
int k = *(u32 *)key;
|
||||
unsigned long flags;
|
||||
int ret = -ENOENT;
|
||||
|
||||
spin_lock_irqsave(&dtab->index_lock, flags);
|
||||
|
||||
old_dev = __dev_map_hash_lookup_elem(map, k);
|
||||
if (old_dev) {
|
||||
dtab->items--;
|
||||
hlist_del_init_rcu(&old_dev->index_hlist);
|
||||
call_rcu(&old_dev->rcu, __dev_map_entry_free);
|
||||
ret = 0;
|
||||
}
|
||||
spin_unlock_irqrestore(&dtab->index_lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct bpf_dtab_netdev *__dev_map_alloc_node(struct net *net,
|
||||
struct bpf_dtab *dtab,
|
||||
u32 ifindex,
|
||||
unsigned int idx)
|
||||
{
|
||||
gfp_t gfp = GFP_ATOMIC | __GFP_NOWARN;
|
||||
struct bpf_dtab_netdev *dev;
|
||||
struct xdp_bulk_queue *bq;
|
||||
int cpu;
|
||||
|
||||
dev = kmalloc_node(sizeof(*dev), gfp, dtab->map.numa_node);
|
||||
if (!dev)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
dev->bulkq = __alloc_percpu_gfp(sizeof(*dev->bulkq),
|
||||
sizeof(void *), gfp);
|
||||
if (!dev->bulkq) {
|
||||
kfree(dev);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
bq = per_cpu_ptr(dev->bulkq, cpu);
|
||||
bq->obj = dev;
|
||||
}
|
||||
|
||||
dev->dev = dev_get_by_index(net, ifindex);
|
||||
if (!dev->dev) {
|
||||
free_percpu(dev->bulkq);
|
||||
kfree(dev);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
dev->idx = idx;
|
||||
dev->dtab = dtab;
|
||||
|
||||
return dev;
|
||||
}
|
||||
|
||||
static int __dev_map_update_elem(struct net *net, struct bpf_map *map,
|
||||
void *key, void *value, u64 map_flags)
|
||||
{
|
||||
struct bpf_dtab *dtab = container_of(map, struct bpf_dtab, map);
|
||||
struct bpf_dtab_netdev *dev, *old_dev;
|
||||
u32 ifindex = *(u32 *)value;
|
||||
struct xdp_bulk_queue *bq;
|
||||
u32 i = *(u32 *)key;
|
||||
int cpu;
|
||||
|
||||
if (unlikely(map_flags > BPF_EXIST))
|
||||
return -EINVAL;
|
||||
|
|
@ -434,31 +619,9 @@ static int dev_map_update_elem(struct bpf_map *map, void *key, void *value,
|
|||
if (!ifindex) {
|
||||
dev = NULL;
|
||||
} else {
|
||||
dev = kmalloc_node(sizeof(*dev), gfp, map->numa_node);
|
||||
if (!dev)
|
||||
return -ENOMEM;
|
||||
|
||||
dev->bulkq = __alloc_percpu_gfp(sizeof(*dev->bulkq),
|
||||
sizeof(void *), gfp);
|
||||
if (!dev->bulkq) {
|
||||
kfree(dev);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
bq = per_cpu_ptr(dev->bulkq, cpu);
|
||||
bq->obj = dev;
|
||||
}
|
||||
|
||||
dev->dev = dev_get_by_index(net, ifindex);
|
||||
if (!dev->dev) {
|
||||
free_percpu(dev->bulkq);
|
||||
kfree(dev);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev->bit = i;
|
||||
dev->dtab = dtab;
|
||||
dev = __dev_map_alloc_node(net, dtab, ifindex, i);
|
||||
if (IS_ERR(dev))
|
||||
return PTR_ERR(dev);
|
||||
}
|
||||
|
||||
/* Use call_rcu() here to ensure rcu critical sections have completed
|
||||
|
|
@ -472,6 +635,70 @@ static int dev_map_update_elem(struct bpf_map *map, void *key, void *value,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int dev_map_update_elem(struct bpf_map *map, void *key, void *value,
|
||||
u64 map_flags)
|
||||
{
|
||||
return __dev_map_update_elem(current->nsproxy->net_ns,
|
||||
map, key, value, map_flags);
|
||||
}
|
||||
|
||||
static int __dev_map_hash_update_elem(struct net *net, struct bpf_map *map,
|
||||
void *key, void *value, u64 map_flags)
|
||||
{
|
||||
struct bpf_dtab *dtab = container_of(map, struct bpf_dtab, map);
|
||||
struct bpf_dtab_netdev *dev, *old_dev;
|
||||
u32 ifindex = *(u32 *)value;
|
||||
u32 idx = *(u32 *)key;
|
||||
unsigned long flags;
|
||||
int err = -EEXIST;
|
||||
|
||||
if (unlikely(map_flags > BPF_EXIST || !ifindex))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&dtab->index_lock, flags);
|
||||
|
||||
old_dev = __dev_map_hash_lookup_elem(map, idx);
|
||||
if (old_dev && (map_flags & BPF_NOEXIST))
|
||||
goto out_err;
|
||||
|
||||
dev = __dev_map_alloc_node(net, dtab, ifindex, idx);
|
||||
if (IS_ERR(dev)) {
|
||||
err = PTR_ERR(dev);
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
if (old_dev) {
|
||||
hlist_del_rcu(&old_dev->index_hlist);
|
||||
} else {
|
||||
if (dtab->items >= dtab->map.max_entries) {
|
||||
spin_unlock_irqrestore(&dtab->index_lock, flags);
|
||||
call_rcu(&dev->rcu, __dev_map_entry_free);
|
||||
return -E2BIG;
|
||||
}
|
||||
dtab->items++;
|
||||
}
|
||||
|
||||
hlist_add_head_rcu(&dev->index_hlist,
|
||||
dev_map_index_hash(dtab, idx));
|
||||
spin_unlock_irqrestore(&dtab->index_lock, flags);
|
||||
|
||||
if (old_dev)
|
||||
call_rcu(&old_dev->rcu, __dev_map_entry_free);
|
||||
|
||||
return 0;
|
||||
|
||||
out_err:
|
||||
spin_unlock_irqrestore(&dtab->index_lock, flags);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int dev_map_hash_update_elem(struct bpf_map *map, void *key, void *value,
|
||||
u64 map_flags)
|
||||
{
|
||||
return __dev_map_hash_update_elem(current->nsproxy->net_ns,
|
||||
map, key, value, map_flags);
|
||||
}
|
||||
|
||||
const struct bpf_map_ops dev_map_ops = {
|
||||
.map_alloc = dev_map_alloc,
|
||||
.map_free = dev_map_free,
|
||||
|
|
@ -482,6 +709,16 @@ const struct bpf_map_ops dev_map_ops = {
|
|||
.map_check_btf = map_check_no_btf,
|
||||
};
|
||||
|
||||
const struct bpf_map_ops dev_map_hash_ops = {
|
||||
.map_alloc = dev_map_alloc,
|
||||
.map_free = dev_map_free,
|
||||
.map_get_next_key = dev_map_hash_get_next_key,
|
||||
.map_lookup_elem = dev_map_hash_lookup_elem,
|
||||
.map_update_elem = dev_map_hash_update_elem,
|
||||
.map_delete_elem = dev_map_hash_delete_elem,
|
||||
.map_check_btf = map_check_no_btf,
|
||||
};
|
||||
|
||||
static int dev_map_notification(struct notifier_block *notifier,
|
||||
ulong event, void *ptr)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -683,8 +683,8 @@ struct bpf_map *bpf_map_get_with_uref(u32 ufd)
|
|||
}
|
||||
|
||||
/* map_idr_lock should have been held */
|
||||
static struct bpf_map *bpf_map_inc_not_zero(struct bpf_map *map,
|
||||
bool uref)
|
||||
static struct bpf_map *__bpf_map_inc_not_zero(struct bpf_map *map,
|
||||
bool uref)
|
||||
{
|
||||
int refold;
|
||||
|
||||
|
|
@ -704,6 +704,16 @@ static struct bpf_map *bpf_map_inc_not_zero(struct bpf_map *map,
|
|||
return map;
|
||||
}
|
||||
|
||||
struct bpf_map *bpf_map_inc_not_zero(struct bpf_map *map, bool uref)
|
||||
{
|
||||
spin_lock_bh(&map_idr_lock);
|
||||
map = __bpf_map_inc_not_zero(map, uref);
|
||||
spin_unlock_bh(&map_idr_lock);
|
||||
|
||||
return map;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(bpf_map_inc_not_zero);
|
||||
|
||||
int __weak bpf_stackmap_copy(struct bpf_map *map, void *key, void *value)
|
||||
{
|
||||
return -ENOTSUPP;
|
||||
|
|
@ -1619,6 +1629,7 @@ static int bpf_prog_load(union bpf_attr *attr, union bpf_attr __user *uattr)
|
|||
|
||||
if (attr->prog_flags & ~(BPF_F_STRICT_ALIGNMENT |
|
||||
BPF_F_ANY_ALIGNMENT |
|
||||
BPF_F_TEST_STATE_FREQ |
|
||||
BPF_F_TEST_RND_HI32))
|
||||
return -EINVAL;
|
||||
|
||||
|
|
@ -2183,7 +2194,7 @@ static int bpf_map_get_fd_by_id(const union bpf_attr *attr)
|
|||
spin_lock_bh(&map_idr_lock);
|
||||
map = idr_find(&map_idr, id);
|
||||
if (map)
|
||||
map = bpf_map_inc_not_zero(map, true);
|
||||
map = __bpf_map_inc_not_zero(map, true);
|
||||
else
|
||||
map = ERR_PTR(-ENOENT);
|
||||
spin_unlock_bh(&map_idr_lock);
|
||||
|
|
@ -2880,6 +2891,10 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz
|
|||
err = bpf_obj_get_next_id(&attr, uattr,
|
||||
&map_idr, &map_idr_lock);
|
||||
break;
|
||||
case BPF_BTF_GET_NEXT_ID:
|
||||
err = bpf_obj_get_next_id(&attr, uattr,
|
||||
&btf_idr, &btf_idr_lock);
|
||||
break;
|
||||
case BPF_PROG_GET_FD_BY_ID:
|
||||
err = bpf_prog_get_fd_by_id(&attr);
|
||||
break;
|
||||
|
|
|
|||
46
kernel/bpf/sysfs_btf.c
Normal file
46
kernel/bpf/sysfs_btf.c
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Provide kernel BTF information for introspection and use by eBPF tools.
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/sysfs.h>
|
||||
|
||||
/* See scripts/link-vmlinux.sh, gen_btf() func for details */
|
||||
extern char __weak _binary__btf_vmlinux_bin_start[];
|
||||
extern char __weak _binary__btf_vmlinux_bin_end[];
|
||||
|
||||
static ssize_t
|
||||
btf_vmlinux_read(struct file *file, struct kobject *kobj,
|
||||
struct bin_attribute *bin_attr,
|
||||
char *buf, loff_t off, size_t len)
|
||||
{
|
||||
memcpy(buf, _binary__btf_vmlinux_bin_start + off, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
static struct bin_attribute bin_attr_btf_vmlinux __ro_after_init = {
|
||||
.attr = { .name = "vmlinux", .mode = 0444, },
|
||||
.read = btf_vmlinux_read,
|
||||
};
|
||||
|
||||
static struct kobject *btf_kobj;
|
||||
|
||||
static int __init btf_vmlinux_init(void)
|
||||
{
|
||||
if (!_binary__btf_vmlinux_bin_start)
|
||||
return 0;
|
||||
|
||||
btf_kobj = kobject_create_and_add("btf", kernel_kobj);
|
||||
if (!btf_kobj)
|
||||
return -ENOMEM;
|
||||
|
||||
bin_attr_btf_vmlinux.size = _binary__btf_vmlinux_bin_end -
|
||||
_binary__btf_vmlinux_bin_start;
|
||||
|
||||
return sysfs_create_bin_file(btf_kobj, &bin_attr_btf_vmlinux);
|
||||
}
|
||||
|
||||
subsys_initcall(btf_vmlinux_init);
|
||||
|
|
@ -3463,6 +3463,7 @@ static int check_map_func_compatibility(struct bpf_verifier_env *env,
|
|||
goto error;
|
||||
break;
|
||||
case BPF_MAP_TYPE_DEVMAP:
|
||||
case BPF_MAP_TYPE_DEVMAP_HASH:
|
||||
if (func_id != BPF_FUNC_redirect_map &&
|
||||
func_id != BPF_FUNC_map_lookup_elem)
|
||||
goto error;
|
||||
|
|
@ -3545,6 +3546,7 @@ static int check_map_func_compatibility(struct bpf_verifier_env *env,
|
|||
break;
|
||||
case BPF_FUNC_redirect_map:
|
||||
if (map->map_type != BPF_MAP_TYPE_DEVMAP &&
|
||||
map->map_type != BPF_MAP_TYPE_DEVMAP_HASH &&
|
||||
map->map_type != BPF_MAP_TYPE_CPUMAP &&
|
||||
map->map_type != BPF_MAP_TYPE_XSKMAP)
|
||||
goto error;
|
||||
|
|
@ -7226,7 +7228,7 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx)
|
|||
struct bpf_verifier_state_list *sl, **pprev;
|
||||
struct bpf_verifier_state *cur = env->cur_state, *new;
|
||||
int i, j, err, states_cnt = 0;
|
||||
bool add_new_state = false;
|
||||
bool add_new_state = env->test_state_freq ? true : false;
|
||||
|
||||
cur->last_insn_idx = env->prev_insn_idx;
|
||||
if (!env->insn_aux_data[insn_idx].prune_point)
|
||||
|
|
@ -8622,8 +8624,8 @@ static int convert_ctx_accesses(struct bpf_verifier_env *env)
|
|||
}
|
||||
|
||||
if (is_narrower_load && size < target_size) {
|
||||
u8 shift = bpf_ctx_narrow_load_shift(off, size,
|
||||
size_default);
|
||||
u8 shift = bpf_ctx_narrow_access_offset(
|
||||
off, size, size_default) * 8;
|
||||
if (ctx_field_size <= 4) {
|
||||
if (shift)
|
||||
insn_buf[cnt++] = BPF_ALU32_IMM(BPF_RSH,
|
||||
|
|
@ -9266,6 +9268,9 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr,
|
|||
|
||||
env->allow_ptr_leaks = is_priv;
|
||||
|
||||
if (is_priv)
|
||||
env->test_state_freq = attr->prog_flags & BPF_F_TEST_STATE_FREQ;
|
||||
|
||||
ret = replace_map_fd_with_map_ptr(env);
|
||||
if (ret < 0)
|
||||
goto skip_full_check;
|
||||
|
|
|
|||
|
|
@ -13,8 +13,71 @@ struct xsk_map {
|
|||
struct bpf_map map;
|
||||
struct xdp_sock **xsk_map;
|
||||
struct list_head __percpu *flush_list;
|
||||
spinlock_t lock; /* Synchronize map updates */
|
||||
};
|
||||
|
||||
int xsk_map_inc(struct xsk_map *map)
|
||||
{
|
||||
struct bpf_map *m = &map->map;
|
||||
|
||||
m = bpf_map_inc(m, false);
|
||||
return PTR_ERR_OR_ZERO(m);
|
||||
}
|
||||
|
||||
void xsk_map_put(struct xsk_map *map)
|
||||
{
|
||||
bpf_map_put(&map->map);
|
||||
}
|
||||
|
||||
static struct xsk_map_node *xsk_map_node_alloc(struct xsk_map *map,
|
||||
struct xdp_sock **map_entry)
|
||||
{
|
||||
struct xsk_map_node *node;
|
||||
int err;
|
||||
|
||||
node = kzalloc(sizeof(*node), GFP_ATOMIC | __GFP_NOWARN);
|
||||
if (!node)
|
||||
return NULL;
|
||||
|
||||
err = xsk_map_inc(map);
|
||||
if (err) {
|
||||
kfree(node);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
node->map = map;
|
||||
node->map_entry = map_entry;
|
||||
return node;
|
||||
}
|
||||
|
||||
static void xsk_map_node_free(struct xsk_map_node *node)
|
||||
{
|
||||
xsk_map_put(node->map);
|
||||
kfree(node);
|
||||
}
|
||||
|
||||
static void xsk_map_sock_add(struct xdp_sock *xs, struct xsk_map_node *node)
|
||||
{
|
||||
spin_lock_bh(&xs->map_list_lock);
|
||||
list_add_tail(&node->node, &xs->map_list);
|
||||
spin_unlock_bh(&xs->map_list_lock);
|
||||
}
|
||||
|
||||
static void xsk_map_sock_delete(struct xdp_sock *xs,
|
||||
struct xdp_sock **map_entry)
|
||||
{
|
||||
struct xsk_map_node *n, *tmp;
|
||||
|
||||
spin_lock_bh(&xs->map_list_lock);
|
||||
list_for_each_entry_safe(n, tmp, &xs->map_list, node) {
|
||||
if (map_entry == n->map_entry) {
|
||||
list_del(&n->node);
|
||||
xsk_map_node_free(n);
|
||||
}
|
||||
}
|
||||
spin_unlock_bh(&xs->map_list_lock);
|
||||
}
|
||||
|
||||
static struct bpf_map *xsk_map_alloc(union bpf_attr *attr)
|
||||
{
|
||||
struct xsk_map *m;
|
||||
|
|
@ -34,6 +97,7 @@ static struct bpf_map *xsk_map_alloc(union bpf_attr *attr)
|
|||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
bpf_map_init_from_attr(&m->map, attr);
|
||||
spin_lock_init(&m->lock);
|
||||
|
||||
cost = (u64)m->map.max_entries * sizeof(struct xdp_sock *);
|
||||
cost += sizeof(struct list_head) * num_possible_cpus();
|
||||
|
|
@ -71,21 +135,9 @@ free_m:
|
|||
static void xsk_map_free(struct bpf_map *map)
|
||||
{
|
||||
struct xsk_map *m = container_of(map, struct xsk_map, map);
|
||||
int i;
|
||||
|
||||
bpf_clear_redirect_map(map);
|
||||
synchronize_net();
|
||||
|
||||
for (i = 0; i < map->max_entries; i++) {
|
||||
struct xdp_sock *xs;
|
||||
|
||||
xs = m->xsk_map[i];
|
||||
if (!xs)
|
||||
continue;
|
||||
|
||||
sock_put((struct sock *)xs);
|
||||
}
|
||||
|
||||
free_percpu(m->flush_list);
|
||||
bpf_map_area_free(m->xsk_map);
|
||||
kfree(m);
|
||||
|
|
@ -164,8 +216,9 @@ static int xsk_map_update_elem(struct bpf_map *map, void *key, void *value,
|
|||
u64 map_flags)
|
||||
{
|
||||
struct xsk_map *m = container_of(map, struct xsk_map, map);
|
||||
struct xdp_sock *xs, *old_xs, **map_entry;
|
||||
u32 i = *(u32 *)key, fd = *(u32 *)value;
|
||||
struct xdp_sock *xs, *old_xs;
|
||||
struct xsk_map_node *node;
|
||||
struct socket *sock;
|
||||
int err;
|
||||
|
||||
|
|
@ -173,8 +226,6 @@ static int xsk_map_update_elem(struct bpf_map *map, void *key, void *value,
|
|||
return -EINVAL;
|
||||
if (unlikely(i >= m->map.max_entries))
|
||||
return -E2BIG;
|
||||
if (unlikely(map_flags == BPF_NOEXIST))
|
||||
return -EEXIST;
|
||||
|
||||
sock = sockfd_lookup(fd, &err);
|
||||
if (!sock)
|
||||
|
|
@ -192,32 +243,70 @@ static int xsk_map_update_elem(struct bpf_map *map, void *key, void *value,
|
|||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
sock_hold(sock->sk);
|
||||
map_entry = &m->xsk_map[i];
|
||||
node = xsk_map_node_alloc(m, map_entry);
|
||||
if (IS_ERR(node)) {
|
||||
sockfd_put(sock);
|
||||
return PTR_ERR(node);
|
||||
}
|
||||
|
||||
old_xs = xchg(&m->xsk_map[i], xs);
|
||||
spin_lock_bh(&m->lock);
|
||||
old_xs = READ_ONCE(*map_entry);
|
||||
if (old_xs == xs) {
|
||||
err = 0;
|
||||
goto out;
|
||||
} else if (old_xs && map_flags == BPF_NOEXIST) {
|
||||
err = -EEXIST;
|
||||
goto out;
|
||||
} else if (!old_xs && map_flags == BPF_EXIST) {
|
||||
err = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
xsk_map_sock_add(xs, node);
|
||||
WRITE_ONCE(*map_entry, xs);
|
||||
if (old_xs)
|
||||
sock_put((struct sock *)old_xs);
|
||||
|
||||
xsk_map_sock_delete(old_xs, map_entry);
|
||||
spin_unlock_bh(&m->lock);
|
||||
sockfd_put(sock);
|
||||
return 0;
|
||||
|
||||
out:
|
||||
spin_unlock_bh(&m->lock);
|
||||
sockfd_put(sock);
|
||||
xsk_map_node_free(node);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int xsk_map_delete_elem(struct bpf_map *map, void *key)
|
||||
{
|
||||
struct xsk_map *m = container_of(map, struct xsk_map, map);
|
||||
struct xdp_sock *old_xs;
|
||||
struct xdp_sock *old_xs, **map_entry;
|
||||
int k = *(u32 *)key;
|
||||
|
||||
if (k >= map->max_entries)
|
||||
return -EINVAL;
|
||||
|
||||
old_xs = xchg(&m->xsk_map[k], NULL);
|
||||
spin_lock_bh(&m->lock);
|
||||
map_entry = &m->xsk_map[k];
|
||||
old_xs = xchg(map_entry, NULL);
|
||||
if (old_xs)
|
||||
sock_put((struct sock *)old_xs);
|
||||
xsk_map_sock_delete(old_xs, map_entry);
|
||||
spin_unlock_bh(&m->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void xsk_map_try_sock_delete(struct xsk_map *map, struct xdp_sock *xs,
|
||||
struct xdp_sock **map_entry)
|
||||
{
|
||||
spin_lock_bh(&map->lock);
|
||||
if (READ_ONCE(*map_entry) == xs) {
|
||||
WRITE_ONCE(*map_entry, NULL);
|
||||
xsk_map_sock_delete(xs, map_entry);
|
||||
}
|
||||
spin_unlock_bh(&map->lock);
|
||||
}
|
||||
|
||||
const struct bpf_map_ops xsk_map_ops = {
|
||||
.map_alloc = xsk_map_alloc,
|
||||
.map_free = xsk_map_free,
|
||||
|
|
|
|||
|
|
@ -193,25 +193,6 @@ struct cgroup_pidlist {
|
|||
struct delayed_work destroy_dwork;
|
||||
};
|
||||
|
||||
/*
|
||||
* The following two functions "fix" the issue where there are more pids
|
||||
* than kmalloc will give memory for; in such cases, we use vmalloc/vfree.
|
||||
* TODO: replace with a kernel-wide solution to this problem
|
||||
*/
|
||||
#define PIDLIST_TOO_LARGE(c) ((c) * sizeof(pid_t) > (PAGE_SIZE * 2))
|
||||
static void *pidlist_allocate(int count)
|
||||
{
|
||||
if (PIDLIST_TOO_LARGE(count))
|
||||
return vmalloc(array_size(count, sizeof(pid_t)));
|
||||
else
|
||||
return kmalloc_array(count, sizeof(pid_t), GFP_KERNEL);
|
||||
}
|
||||
|
||||
static void pidlist_free(void *p)
|
||||
{
|
||||
kvfree(p);
|
||||
}
|
||||
|
||||
/*
|
||||
* Used to destroy all pidlists lingering waiting for destroy timer. None
|
||||
* should be left afterwards.
|
||||
|
|
@ -244,7 +225,7 @@ static void cgroup_pidlist_destroy_work_fn(struct work_struct *work)
|
|||
*/
|
||||
if (!delayed_work_pending(dwork)) {
|
||||
list_del(&l->links);
|
||||
pidlist_free(l->list);
|
||||
kvfree(l->list);
|
||||
put_pid_ns(l->key.ns);
|
||||
tofree = l;
|
||||
}
|
||||
|
|
@ -365,7 +346,7 @@ static int pidlist_array_load(struct cgroup *cgrp, enum cgroup_filetype type,
|
|||
* show up until sometime later on.
|
||||
*/
|
||||
length = cgroup_task_count(cgrp);
|
||||
array = pidlist_allocate(length);
|
||||
array = kvmalloc_array(length, sizeof(pid_t), GFP_KERNEL);
|
||||
if (!array)
|
||||
return -ENOMEM;
|
||||
/* now, populate the array */
|
||||
|
|
@ -390,12 +371,12 @@ static int pidlist_array_load(struct cgroup *cgrp, enum cgroup_filetype type,
|
|||
|
||||
l = cgroup_pidlist_find_create(cgrp, type);
|
||||
if (!l) {
|
||||
pidlist_free(array);
|
||||
kvfree(array);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* store array, freeing old if necessary */
|
||||
pidlist_free(l->list);
|
||||
kvfree(l->list);
|
||||
l->list = array;
|
||||
l->length = length;
|
||||
*lp = l;
|
||||
|
|
|
|||
|
|
@ -488,7 +488,7 @@ static struct cgroup_subsys_state *cgroup_tryget_css(struct cgroup *cgrp,
|
|||
|
||||
rcu_read_lock();
|
||||
css = cgroup_css(cgrp, ss);
|
||||
if (!css || !css_tryget_online(css))
|
||||
if (css && !css_tryget_online(css))
|
||||
css = NULL;
|
||||
rcu_read_unlock();
|
||||
|
||||
|
|
@ -1891,7 +1891,7 @@ static int cgroup_reconfigure(struct fs_context *fc)
|
|||
*/
|
||||
static bool use_task_css_set_links __read_mostly;
|
||||
|
||||
static void cgroup_enable_task_cg_lists(void)
|
||||
void cgroup_enable_task_cg_lists(void)
|
||||
{
|
||||
struct task_struct *p, *g;
|
||||
|
||||
|
|
@ -2894,7 +2894,7 @@ static void cgroup_print_ss_mask(struct seq_file *seq, u16 ss_mask)
|
|||
do_each_subsys_mask(ss, ssid, ss_mask) {
|
||||
if (printed)
|
||||
seq_putc(seq, ' ');
|
||||
seq_printf(seq, "%s", ss->name);
|
||||
seq_puts(seq, ss->name);
|
||||
printed = true;
|
||||
} while_each_subsys_mask();
|
||||
if (printed)
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@
|
|||
#include <linux/proc_fs.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/sched/deadline.h>
|
||||
#include <linux/sched/mm.h>
|
||||
#include <linux/sched/task.h>
|
||||
#include <linux/seq_file.h>
|
||||
|
|
@ -332,7 +333,18 @@ static struct cpuset top_cpuset = {
|
|||
* guidelines for accessing subsystem state in kernel/cgroup.c
|
||||
*/
|
||||
|
||||
static DEFINE_MUTEX(cpuset_mutex);
|
||||
DEFINE_STATIC_PERCPU_RWSEM(cpuset_rwsem);
|
||||
|
||||
void cpuset_read_lock(void)
|
||||
{
|
||||
percpu_down_read(&cpuset_rwsem);
|
||||
}
|
||||
|
||||
void cpuset_read_unlock(void)
|
||||
{
|
||||
percpu_up_read(&cpuset_rwsem);
|
||||
}
|
||||
|
||||
static DEFINE_SPINLOCK(callback_lock);
|
||||
|
||||
static struct workqueue_struct *cpuset_migrate_mm_wq;
|
||||
|
|
@ -894,6 +906,67 @@ done:
|
|||
return ndoms;
|
||||
}
|
||||
|
||||
static void update_tasks_root_domain(struct cpuset *cs)
|
||||
{
|
||||
struct css_task_iter it;
|
||||
struct task_struct *task;
|
||||
|
||||
css_task_iter_start(&cs->css, 0, &it);
|
||||
|
||||
while ((task = css_task_iter_next(&it)))
|
||||
dl_add_task_root_domain(task);
|
||||
|
||||
css_task_iter_end(&it);
|
||||
}
|
||||
|
||||
static void rebuild_root_domains(void)
|
||||
{
|
||||
struct cpuset *cs = NULL;
|
||||
struct cgroup_subsys_state *pos_css;
|
||||
|
||||
percpu_rwsem_assert_held(&cpuset_rwsem);
|
||||
lockdep_assert_cpus_held();
|
||||
lockdep_assert_held(&sched_domains_mutex);
|
||||
|
||||
cgroup_enable_task_cg_lists();
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
/*
|
||||
* Clear default root domain DL accounting, it will be computed again
|
||||
* if a task belongs to it.
|
||||
*/
|
||||
dl_clear_root_domain(&def_root_domain);
|
||||
|
||||
cpuset_for_each_descendant_pre(cs, pos_css, &top_cpuset) {
|
||||
|
||||
if (cpumask_empty(cs->effective_cpus)) {
|
||||
pos_css = css_rightmost_descendant(pos_css);
|
||||
continue;
|
||||
}
|
||||
|
||||
css_get(&cs->css);
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
update_tasks_root_domain(cs);
|
||||
|
||||
rcu_read_lock();
|
||||
css_put(&cs->css);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
static void
|
||||
partition_and_rebuild_sched_domains(int ndoms_new, cpumask_var_t doms_new[],
|
||||
struct sched_domain_attr *dattr_new)
|
||||
{
|
||||
mutex_lock(&sched_domains_mutex);
|
||||
partition_sched_domains_locked(ndoms_new, doms_new, dattr_new);
|
||||
rebuild_root_domains();
|
||||
mutex_unlock(&sched_domains_mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* Rebuild scheduler domains.
|
||||
*
|
||||
|
|
@ -911,8 +984,8 @@ static void rebuild_sched_domains_locked(void)
|
|||
cpumask_var_t *doms;
|
||||
int ndoms;
|
||||
|
||||
lockdep_assert_held(&cpuset_mutex);
|
||||
get_online_cpus();
|
||||
lockdep_assert_cpus_held();
|
||||
percpu_rwsem_assert_held(&cpuset_rwsem);
|
||||
|
||||
/*
|
||||
* We have raced with CPU hotplug. Don't do anything to avoid
|
||||
|
|
@ -921,19 +994,17 @@ static void rebuild_sched_domains_locked(void)
|
|||
*/
|
||||
if (!top_cpuset.nr_subparts_cpus &&
|
||||
!cpumask_equal(top_cpuset.effective_cpus, cpu_active_mask))
|
||||
goto out;
|
||||
return;
|
||||
|
||||
if (top_cpuset.nr_subparts_cpus &&
|
||||
!cpumask_subset(top_cpuset.effective_cpus, cpu_active_mask))
|
||||
goto out;
|
||||
return;
|
||||
|
||||
/* Generate domain masks and attrs */
|
||||
ndoms = generate_sched_domains(&doms, &attr);
|
||||
|
||||
/* Have scheduler rebuild the domains */
|
||||
partition_sched_domains(ndoms, doms, attr);
|
||||
out:
|
||||
put_online_cpus();
|
||||
partition_and_rebuild_sched_domains(ndoms, doms, attr);
|
||||
}
|
||||
#else /* !CONFIG_SMP */
|
||||
static void rebuild_sched_domains_locked(void)
|
||||
|
|
@ -943,9 +1014,11 @@ static void rebuild_sched_domains_locked(void)
|
|||
|
||||
void rebuild_sched_domains(void)
|
||||
{
|
||||
mutex_lock(&cpuset_mutex);
|
||||
get_online_cpus();
|
||||
percpu_down_write(&cpuset_rwsem);
|
||||
rebuild_sched_domains_locked();
|
||||
mutex_unlock(&cpuset_mutex);
|
||||
percpu_up_write(&cpuset_rwsem);
|
||||
put_online_cpus();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1051,7 +1124,7 @@ static int update_parent_subparts_cpumask(struct cpuset *cpuset, int cmd,
|
|||
int deleting; /* Moving cpus from subparts_cpus to effective_cpus */
|
||||
bool part_error = false; /* Partition error? */
|
||||
|
||||
lockdep_assert_held(&cpuset_mutex);
|
||||
percpu_rwsem_assert_held(&cpuset_rwsem);
|
||||
|
||||
/*
|
||||
* The parent must be a partition root.
|
||||
|
|
@ -2039,7 +2112,7 @@ static int cpuset_can_attach(struct cgroup_taskset *tset)
|
|||
cpuset_attach_old_cs = task_cs(cgroup_taskset_first(tset, &css));
|
||||
cs = css_cs(css);
|
||||
|
||||
mutex_lock(&cpuset_mutex);
|
||||
percpu_down_write(&cpuset_rwsem);
|
||||
|
||||
/* allow moving tasks into an empty cpuset if on default hierarchy */
|
||||
ret = -ENOSPC;
|
||||
|
|
@ -2063,7 +2136,7 @@ static int cpuset_can_attach(struct cgroup_taskset *tset)
|
|||
cs->attach_in_progress++;
|
||||
ret = 0;
|
||||
out_unlock:
|
||||
mutex_unlock(&cpuset_mutex);
|
||||
percpu_up_write(&cpuset_rwsem);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -2073,9 +2146,9 @@ static void cpuset_cancel_attach(struct cgroup_taskset *tset)
|
|||
|
||||
cgroup_taskset_first(tset, &css);
|
||||
|
||||
mutex_lock(&cpuset_mutex);
|
||||
percpu_down_write(&cpuset_rwsem);
|
||||
css_cs(css)->attach_in_progress--;
|
||||
mutex_unlock(&cpuset_mutex);
|
||||
percpu_up_write(&cpuset_rwsem);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -2098,7 +2171,7 @@ static void cpuset_attach(struct cgroup_taskset *tset)
|
|||
cgroup_taskset_first(tset, &css);
|
||||
cs = css_cs(css);
|
||||
|
||||
mutex_lock(&cpuset_mutex);
|
||||
percpu_down_write(&cpuset_rwsem);
|
||||
|
||||
/* prepare for attach */
|
||||
if (cs == &top_cpuset)
|
||||
|
|
@ -2152,7 +2225,7 @@ static void cpuset_attach(struct cgroup_taskset *tset)
|
|||
if (!cs->attach_in_progress)
|
||||
wake_up(&cpuset_attach_wq);
|
||||
|
||||
mutex_unlock(&cpuset_mutex);
|
||||
percpu_up_write(&cpuset_rwsem);
|
||||
}
|
||||
|
||||
/* The various types of files and directories in a cpuset file system */
|
||||
|
|
@ -2183,7 +2256,8 @@ static int cpuset_write_u64(struct cgroup_subsys_state *css, struct cftype *cft,
|
|||
cpuset_filetype_t type = cft->private;
|
||||
int retval = 0;
|
||||
|
||||
mutex_lock(&cpuset_mutex);
|
||||
get_online_cpus();
|
||||
percpu_down_write(&cpuset_rwsem);
|
||||
if (!is_cpuset_online(cs)) {
|
||||
retval = -ENODEV;
|
||||
goto out_unlock;
|
||||
|
|
@ -2219,7 +2293,8 @@ static int cpuset_write_u64(struct cgroup_subsys_state *css, struct cftype *cft,
|
|||
break;
|
||||
}
|
||||
out_unlock:
|
||||
mutex_unlock(&cpuset_mutex);
|
||||
percpu_up_write(&cpuset_rwsem);
|
||||
put_online_cpus();
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
@ -2230,7 +2305,8 @@ static int cpuset_write_s64(struct cgroup_subsys_state *css, struct cftype *cft,
|
|||
cpuset_filetype_t type = cft->private;
|
||||
int retval = -ENODEV;
|
||||
|
||||
mutex_lock(&cpuset_mutex);
|
||||
get_online_cpus();
|
||||
percpu_down_write(&cpuset_rwsem);
|
||||
if (!is_cpuset_online(cs))
|
||||
goto out_unlock;
|
||||
|
||||
|
|
@ -2243,7 +2319,8 @@ static int cpuset_write_s64(struct cgroup_subsys_state *css, struct cftype *cft,
|
|||
break;
|
||||
}
|
||||
out_unlock:
|
||||
mutex_unlock(&cpuset_mutex);
|
||||
percpu_up_write(&cpuset_rwsem);
|
||||
put_online_cpus();
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
@ -2282,7 +2359,8 @@ static ssize_t cpuset_write_resmask(struct kernfs_open_file *of,
|
|||
kernfs_break_active_protection(of->kn);
|
||||
flush_work(&cpuset_hotplug_work);
|
||||
|
||||
mutex_lock(&cpuset_mutex);
|
||||
get_online_cpus();
|
||||
percpu_down_write(&cpuset_rwsem);
|
||||
if (!is_cpuset_online(cs))
|
||||
goto out_unlock;
|
||||
|
||||
|
|
@ -2306,7 +2384,8 @@ static ssize_t cpuset_write_resmask(struct kernfs_open_file *of,
|
|||
|
||||
free_cpuset(trialcs);
|
||||
out_unlock:
|
||||
mutex_unlock(&cpuset_mutex);
|
||||
percpu_up_write(&cpuset_rwsem);
|
||||
put_online_cpus();
|
||||
kernfs_unbreak_active_protection(of->kn);
|
||||
css_put(&cs->css);
|
||||
flush_workqueue(cpuset_migrate_mm_wq);
|
||||
|
|
@ -2437,13 +2516,15 @@ static ssize_t sched_partition_write(struct kernfs_open_file *of, char *buf,
|
|||
return -EINVAL;
|
||||
|
||||
css_get(&cs->css);
|
||||
mutex_lock(&cpuset_mutex);
|
||||
get_online_cpus();
|
||||
percpu_down_write(&cpuset_rwsem);
|
||||
if (!is_cpuset_online(cs))
|
||||
goto out_unlock;
|
||||
|
||||
retval = update_prstate(cs, val);
|
||||
out_unlock:
|
||||
mutex_unlock(&cpuset_mutex);
|
||||
percpu_up_write(&cpuset_rwsem);
|
||||
put_online_cpus();
|
||||
css_put(&cs->css);
|
||||
return retval ?: nbytes;
|
||||
}
|
||||
|
|
@ -2649,7 +2730,8 @@ static int cpuset_css_online(struct cgroup_subsys_state *css)
|
|||
if (!parent)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&cpuset_mutex);
|
||||
get_online_cpus();
|
||||
percpu_down_write(&cpuset_rwsem);
|
||||
|
||||
set_bit(CS_ONLINE, &cs->flags);
|
||||
if (is_spread_page(parent))
|
||||
|
|
@ -2700,7 +2782,8 @@ static int cpuset_css_online(struct cgroup_subsys_state *css)
|
|||
cpumask_copy(cs->effective_cpus, parent->cpus_allowed);
|
||||
spin_unlock_irq(&callback_lock);
|
||||
out_unlock:
|
||||
mutex_unlock(&cpuset_mutex);
|
||||
percpu_up_write(&cpuset_rwsem);
|
||||
put_online_cpus();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -2719,7 +2802,8 @@ static void cpuset_css_offline(struct cgroup_subsys_state *css)
|
|||
{
|
||||
struct cpuset *cs = css_cs(css);
|
||||
|
||||
mutex_lock(&cpuset_mutex);
|
||||
get_online_cpus();
|
||||
percpu_down_write(&cpuset_rwsem);
|
||||
|
||||
if (is_partition_root(cs))
|
||||
update_prstate(cs, 0);
|
||||
|
|
@ -2738,7 +2822,8 @@ static void cpuset_css_offline(struct cgroup_subsys_state *css)
|
|||
cpuset_dec();
|
||||
clear_bit(CS_ONLINE, &cs->flags);
|
||||
|
||||
mutex_unlock(&cpuset_mutex);
|
||||
percpu_up_write(&cpuset_rwsem);
|
||||
put_online_cpus();
|
||||
}
|
||||
|
||||
static void cpuset_css_free(struct cgroup_subsys_state *css)
|
||||
|
|
@ -2750,7 +2835,7 @@ static void cpuset_css_free(struct cgroup_subsys_state *css)
|
|||
|
||||
static void cpuset_bind(struct cgroup_subsys_state *root_css)
|
||||
{
|
||||
mutex_lock(&cpuset_mutex);
|
||||
percpu_down_write(&cpuset_rwsem);
|
||||
spin_lock_irq(&callback_lock);
|
||||
|
||||
if (is_in_v2_mode()) {
|
||||
|
|
@ -2763,7 +2848,7 @@ static void cpuset_bind(struct cgroup_subsys_state *root_css)
|
|||
}
|
||||
|
||||
spin_unlock_irq(&callback_lock);
|
||||
mutex_unlock(&cpuset_mutex);
|
||||
percpu_up_write(&cpuset_rwsem);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -2805,6 +2890,8 @@ struct cgroup_subsys cpuset_cgrp_subsys = {
|
|||
|
||||
int __init cpuset_init(void)
|
||||
{
|
||||
BUG_ON(percpu_init_rwsem(&cpuset_rwsem));
|
||||
|
||||
BUG_ON(!alloc_cpumask_var(&top_cpuset.cpus_allowed, GFP_KERNEL));
|
||||
BUG_ON(!alloc_cpumask_var(&top_cpuset.effective_cpus, GFP_KERNEL));
|
||||
BUG_ON(!zalloc_cpumask_var(&top_cpuset.subparts_cpus, GFP_KERNEL));
|
||||
|
|
@ -2876,7 +2963,7 @@ hotplug_update_tasks_legacy(struct cpuset *cs,
|
|||
is_empty = cpumask_empty(cs->cpus_allowed) ||
|
||||
nodes_empty(cs->mems_allowed);
|
||||
|
||||
mutex_unlock(&cpuset_mutex);
|
||||
percpu_up_write(&cpuset_rwsem);
|
||||
|
||||
/*
|
||||
* Move tasks to the nearest ancestor with execution resources,
|
||||
|
|
@ -2886,7 +2973,7 @@ hotplug_update_tasks_legacy(struct cpuset *cs,
|
|||
if (is_empty)
|
||||
remove_tasks_in_empty_cpuset(cs);
|
||||
|
||||
mutex_lock(&cpuset_mutex);
|
||||
percpu_down_write(&cpuset_rwsem);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -2936,14 +3023,14 @@ static void cpuset_hotplug_update_tasks(struct cpuset *cs, struct tmpmasks *tmp)
|
|||
retry:
|
||||
wait_event(cpuset_attach_wq, cs->attach_in_progress == 0);
|
||||
|
||||
mutex_lock(&cpuset_mutex);
|
||||
percpu_down_write(&cpuset_rwsem);
|
||||
|
||||
/*
|
||||
* We have raced with task attaching. We wait until attaching
|
||||
* is finished, so we won't attach a task to an empty cpuset.
|
||||
*/
|
||||
if (cs->attach_in_progress) {
|
||||
mutex_unlock(&cpuset_mutex);
|
||||
percpu_up_write(&cpuset_rwsem);
|
||||
goto retry;
|
||||
}
|
||||
|
||||
|
|
@ -3011,7 +3098,7 @@ update_tasks:
|
|||
hotplug_update_tasks_legacy(cs, &new_cpus, &new_mems,
|
||||
cpus_updated, mems_updated);
|
||||
|
||||
mutex_unlock(&cpuset_mutex);
|
||||
percpu_up_write(&cpuset_rwsem);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -3041,7 +3128,7 @@ static void cpuset_hotplug_workfn(struct work_struct *work)
|
|||
if (on_dfl && !alloc_cpumasks(NULL, &tmp))
|
||||
ptmp = &tmp;
|
||||
|
||||
mutex_lock(&cpuset_mutex);
|
||||
percpu_down_write(&cpuset_rwsem);
|
||||
|
||||
/* fetch the available cpus/mems and find out which changed how */
|
||||
cpumask_copy(&new_cpus, cpu_active_mask);
|
||||
|
|
@ -3091,7 +3178,7 @@ static void cpuset_hotplug_workfn(struct work_struct *work)
|
|||
update_tasks_nodemask(&top_cpuset);
|
||||
}
|
||||
|
||||
mutex_unlock(&cpuset_mutex);
|
||||
percpu_up_write(&cpuset_rwsem);
|
||||
|
||||
/* if cpus or mems changed, we need to propagate to descendants */
|
||||
if (cpus_updated || mems_updated) {
|
||||
|
|
|
|||
35
kernel/cpu.c
35
kernel/cpu.c
|
|
@ -62,7 +62,6 @@ struct cpuhp_cpu_state {
|
|||
bool rollback;
|
||||
bool single;
|
||||
bool bringup;
|
||||
bool booted_once;
|
||||
struct hlist_node *node;
|
||||
struct hlist_node *last;
|
||||
enum cpuhp_state cb_state;
|
||||
|
|
@ -76,6 +75,10 @@ static DEFINE_PER_CPU(struct cpuhp_cpu_state, cpuhp_state) = {
|
|||
.fail = CPUHP_INVALID,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
cpumask_t cpus_booted_once_mask;
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_LOCKDEP) && defined(CONFIG_SMP)
|
||||
static struct lockdep_map cpuhp_state_up_map =
|
||||
STATIC_LOCKDEP_MAP_INIT("cpuhp_state-up", &cpuhp_state_up_map);
|
||||
|
|
@ -433,7 +436,7 @@ static inline bool cpu_smt_allowed(unsigned int cpu)
|
|||
* CPU. Otherwise, a broadacasted MCE observing CR4.MCE=0b on any
|
||||
* core will shutdown the machine.
|
||||
*/
|
||||
return !per_cpu(cpuhp_state, cpu).booted_once;
|
||||
return !cpumask_test_cpu(cpu, &cpus_booted_once_mask);
|
||||
}
|
||||
#else
|
||||
static inline bool cpu_smt_allowed(unsigned int cpu) { return true; }
|
||||
|
|
@ -1066,7 +1069,7 @@ void notify_cpu_starting(unsigned int cpu)
|
|||
int ret;
|
||||
|
||||
rcu_cpu_starting(cpu); /* Enables RCU usage on this CPU. */
|
||||
st->booted_once = true;
|
||||
cpumask_set_cpu(cpu, &cpus_booted_once_mask);
|
||||
while (st->state < target) {
|
||||
st->state++;
|
||||
ret = cpuhp_invoke_callback(cpu, st->state, true, NULL, NULL);
|
||||
|
|
@ -2302,6 +2305,9 @@ EXPORT_SYMBOL(__cpu_present_mask);
|
|||
struct cpumask __cpu_active_mask __read_mostly;
|
||||
EXPORT_SYMBOL(__cpu_active_mask);
|
||||
|
||||
atomic_t __num_online_cpus __read_mostly;
|
||||
EXPORT_SYMBOL(__num_online_cpus);
|
||||
|
||||
void init_cpu_present(const struct cpumask *src)
|
||||
{
|
||||
cpumask_copy(&__cpu_present_mask, src);
|
||||
|
|
@ -2317,6 +2323,27 @@ void init_cpu_online(const struct cpumask *src)
|
|||
cpumask_copy(&__cpu_online_mask, src);
|
||||
}
|
||||
|
||||
void set_cpu_online(unsigned int cpu, bool online)
|
||||
{
|
||||
/*
|
||||
* atomic_inc/dec() is required to handle the horrid abuse of this
|
||||
* function by the reboot and kexec code which invoke it from
|
||||
* IPI/NMI broadcasts when shutting down CPUs. Invocation from
|
||||
* regular CPU hotplug is properly serialized.
|
||||
*
|
||||
* Note, that the fact that __num_online_cpus is of type atomic_t
|
||||
* does not protect readers which are not serialized against
|
||||
* concurrent hotplug operations.
|
||||
*/
|
||||
if (online) {
|
||||
if (!cpumask_test_and_set_cpu(cpu, &__cpu_online_mask))
|
||||
atomic_inc(&__num_online_cpus);
|
||||
} else {
|
||||
if (cpumask_test_and_clear_cpu(cpu, &__cpu_online_mask))
|
||||
atomic_dec(&__num_online_cpus);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Activate the first processor.
|
||||
*/
|
||||
|
|
@ -2341,7 +2368,7 @@ void __init boot_cpu_init(void)
|
|||
void __init boot_cpu_hotplug_init(void)
|
||||
{
|
||||
#ifdef CONFIG_SMP
|
||||
this_cpu_write(cpuhp_state.booted_once, true);
|
||||
cpumask_set_cpu(smp_processor_id(), &cpus_booted_once_mask);
|
||||
#endif
|
||||
this_cpu_write(cpuhp_state.state, CPUHP_ONLINE);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -305,7 +305,7 @@ void dma_direct_unmap_page(struct device *dev, dma_addr_t addr,
|
|||
dma_direct_sync_single_for_cpu(dev, addr, size, dir);
|
||||
|
||||
if (unlikely(is_swiotlb_buffer(phys)))
|
||||
swiotlb_tbl_unmap_single(dev, phys, size, dir, attrs);
|
||||
swiotlb_tbl_unmap_single(dev, phys, size, size, dir, attrs);
|
||||
}
|
||||
EXPORT_SYMBOL(dma_direct_unmap_page);
|
||||
|
||||
|
|
|
|||
|
|
@ -444,7 +444,9 @@ static void swiotlb_bounce(phys_addr_t orig_addr, phys_addr_t tlb_addr,
|
|||
|
||||
phys_addr_t swiotlb_tbl_map_single(struct device *hwdev,
|
||||
dma_addr_t tbl_dma_addr,
|
||||
phys_addr_t orig_addr, size_t size,
|
||||
phys_addr_t orig_addr,
|
||||
size_t mapping_size,
|
||||
size_t alloc_size,
|
||||
enum dma_data_direction dir,
|
||||
unsigned long attrs)
|
||||
{
|
||||
|
|
@ -464,6 +466,12 @@ phys_addr_t swiotlb_tbl_map_single(struct device *hwdev,
|
|||
pr_warn_once("%s is active and system is using DMA bounce buffers\n",
|
||||
sme_active() ? "SME" : "SEV");
|
||||
|
||||
if (mapping_size > alloc_size) {
|
||||
dev_warn_once(hwdev, "Invalid sizes (mapping: %zd bytes, alloc: %zd bytes)",
|
||||
mapping_size, alloc_size);
|
||||
return (phys_addr_t)DMA_MAPPING_ERROR;
|
||||
}
|
||||
|
||||
mask = dma_get_seg_boundary(hwdev);
|
||||
|
||||
tbl_dma_addr &= mask;
|
||||
|
|
@ -471,8 +479,8 @@ phys_addr_t swiotlb_tbl_map_single(struct device *hwdev,
|
|||
offset_slots = ALIGN(tbl_dma_addr, 1 << IO_TLB_SHIFT) >> IO_TLB_SHIFT;
|
||||
|
||||
/*
|
||||
* Carefully handle integer overflow which can occur when mask == ~0UL.
|
||||
*/
|
||||
* Carefully handle integer overflow which can occur when mask == ~0UL.
|
||||
*/
|
||||
max_slots = mask + 1
|
||||
? ALIGN(mask + 1, 1 << IO_TLB_SHIFT) >> IO_TLB_SHIFT
|
||||
: 1UL << (BITS_PER_LONG - IO_TLB_SHIFT);
|
||||
|
|
@ -481,8 +489,8 @@ phys_addr_t swiotlb_tbl_map_single(struct device *hwdev,
|
|||
* For mappings greater than or equal to a page, we limit the stride
|
||||
* (and hence alignment) to a page size.
|
||||
*/
|
||||
nslots = ALIGN(size, 1 << IO_TLB_SHIFT) >> IO_TLB_SHIFT;
|
||||
if (size >= PAGE_SIZE)
|
||||
nslots = ALIGN(alloc_size, 1 << IO_TLB_SHIFT) >> IO_TLB_SHIFT;
|
||||
if (alloc_size >= PAGE_SIZE)
|
||||
stride = (1 << (PAGE_SHIFT - IO_TLB_SHIFT));
|
||||
else
|
||||
stride = 1;
|
||||
|
|
@ -547,7 +555,7 @@ not_found:
|
|||
spin_unlock_irqrestore(&io_tlb_lock, flags);
|
||||
if (!(attrs & DMA_ATTR_NO_WARN) && printk_ratelimit())
|
||||
dev_warn(hwdev, "swiotlb buffer is full (sz: %zd bytes), total %lu (slots), used %lu (slots)\n",
|
||||
size, io_tlb_nslabs, tmp_io_tlb_used);
|
||||
alloc_size, io_tlb_nslabs, tmp_io_tlb_used);
|
||||
return (phys_addr_t)DMA_MAPPING_ERROR;
|
||||
found:
|
||||
io_tlb_used += nslots;
|
||||
|
|
@ -562,7 +570,7 @@ found:
|
|||
io_tlb_orig_addr[index+i] = orig_addr + (i << IO_TLB_SHIFT);
|
||||
if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC) &&
|
||||
(dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL))
|
||||
swiotlb_bounce(orig_addr, tlb_addr, size, DMA_TO_DEVICE);
|
||||
swiotlb_bounce(orig_addr, tlb_addr, mapping_size, DMA_TO_DEVICE);
|
||||
|
||||
return tlb_addr;
|
||||
}
|
||||
|
|
@ -571,11 +579,11 @@ found:
|
|||
* tlb_addr is the physical address of the bounce buffer to unmap.
|
||||
*/
|
||||
void swiotlb_tbl_unmap_single(struct device *hwdev, phys_addr_t tlb_addr,
|
||||
size_t size, enum dma_data_direction dir,
|
||||
unsigned long attrs)
|
||||
size_t mapping_size, size_t alloc_size,
|
||||
enum dma_data_direction dir, unsigned long attrs)
|
||||
{
|
||||
unsigned long flags;
|
||||
int i, count, nslots = ALIGN(size, 1 << IO_TLB_SHIFT) >> IO_TLB_SHIFT;
|
||||
int i, count, nslots = ALIGN(alloc_size, 1 << IO_TLB_SHIFT) >> IO_TLB_SHIFT;
|
||||
int index = (tlb_addr - io_tlb_start) >> IO_TLB_SHIFT;
|
||||
phys_addr_t orig_addr = io_tlb_orig_addr[index];
|
||||
|
||||
|
|
@ -585,7 +593,7 @@ void swiotlb_tbl_unmap_single(struct device *hwdev, phys_addr_t tlb_addr,
|
|||
if (orig_addr != INVALID_PHYS_ADDR &&
|
||||
!(attrs & DMA_ATTR_SKIP_CPU_SYNC) &&
|
||||
((dir == DMA_FROM_DEVICE) || (dir == DMA_BIDIRECTIONAL)))
|
||||
swiotlb_bounce(orig_addr, tlb_addr, size, DMA_FROM_DEVICE);
|
||||
swiotlb_bounce(orig_addr, tlb_addr, mapping_size, DMA_FROM_DEVICE);
|
||||
|
||||
/*
|
||||
* Return the buffer to the free list by setting the corresponding
|
||||
|
|
@ -665,14 +673,14 @@ bool swiotlb_map(struct device *dev, phys_addr_t *phys, dma_addr_t *dma_addr,
|
|||
|
||||
/* Oh well, have to allocate and map a bounce buffer. */
|
||||
*phys = swiotlb_tbl_map_single(dev, __phys_to_dma(dev, io_tlb_start),
|
||||
*phys, size, dir, attrs);
|
||||
*phys, size, size, dir, attrs);
|
||||
if (*phys == (phys_addr_t)DMA_MAPPING_ERROR)
|
||||
return false;
|
||||
|
||||
/* Ensure that the address returned is DMA'ble */
|
||||
*dma_addr = __phys_to_dma(dev, *phys);
|
||||
if (unlikely(!dma_capable(dev, *dma_addr, size))) {
|
||||
swiotlb_tbl_unmap_single(dev, *phys, size, dir,
|
||||
swiotlb_tbl_unmap_single(dev, *phys, size, size, dir,
|
||||
attrs | DMA_ATTR_SKIP_CPU_SYNC);
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1108,7 +1108,7 @@ static void __perf_mux_hrtimer_init(struct perf_cpu_context *cpuctx, int cpu)
|
|||
cpuctx->hrtimer_interval = ns_to_ktime(NSEC_PER_MSEC * interval);
|
||||
|
||||
raw_spin_lock_init(&cpuctx->hrtimer_lock);
|
||||
hrtimer_init(timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_PINNED);
|
||||
hrtimer_init(timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_PINNED_HARD);
|
||||
timer->function = perf_mux_hrtimer_handler;
|
||||
}
|
||||
|
||||
|
|
@ -1126,7 +1126,7 @@ static int perf_mux_hrtimer_restart(struct perf_cpu_context *cpuctx)
|
|||
if (!cpuctx->hrtimer_active) {
|
||||
cpuctx->hrtimer_active = 1;
|
||||
hrtimer_forward_now(timer, cpuctx->hrtimer_interval);
|
||||
hrtimer_start_expires(timer, HRTIMER_MODE_ABS_PINNED);
|
||||
hrtimer_start_expires(timer, HRTIMER_MODE_ABS_PINNED_HARD);
|
||||
}
|
||||
raw_spin_unlock_irqrestore(&cpuctx->hrtimer_lock, flags);
|
||||
|
||||
|
|
@ -1892,6 +1892,89 @@ list_del_event(struct perf_event *event, struct perf_event_context *ctx)
|
|||
ctx->generation++;
|
||||
}
|
||||
|
||||
static int
|
||||
perf_aux_output_match(struct perf_event *event, struct perf_event *aux_event)
|
||||
{
|
||||
if (!has_aux(aux_event))
|
||||
return 0;
|
||||
|
||||
if (!event->pmu->aux_output_match)
|
||||
return 0;
|
||||
|
||||
return event->pmu->aux_output_match(aux_event);
|
||||
}
|
||||
|
||||
static void put_event(struct perf_event *event);
|
||||
static void event_sched_out(struct perf_event *event,
|
||||
struct perf_cpu_context *cpuctx,
|
||||
struct perf_event_context *ctx);
|
||||
|
||||
static void perf_put_aux_event(struct perf_event *event)
|
||||
{
|
||||
struct perf_event_context *ctx = event->ctx;
|
||||
struct perf_cpu_context *cpuctx = __get_cpu_context(ctx);
|
||||
struct perf_event *iter;
|
||||
|
||||
/*
|
||||
* If event uses aux_event tear down the link
|
||||
*/
|
||||
if (event->aux_event) {
|
||||
iter = event->aux_event;
|
||||
event->aux_event = NULL;
|
||||
put_event(iter);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the event is an aux_event, tear down all links to
|
||||
* it from other events.
|
||||
*/
|
||||
for_each_sibling_event(iter, event->group_leader) {
|
||||
if (iter->aux_event != event)
|
||||
continue;
|
||||
|
||||
iter->aux_event = NULL;
|
||||
put_event(event);
|
||||
|
||||
/*
|
||||
* If it's ACTIVE, schedule it out and put it into ERROR
|
||||
* state so that we don't try to schedule it again. Note
|
||||
* that perf_event_enable() will clear the ERROR status.
|
||||
*/
|
||||
event_sched_out(iter, cpuctx, ctx);
|
||||
perf_event_set_state(event, PERF_EVENT_STATE_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
static int perf_get_aux_event(struct perf_event *event,
|
||||
struct perf_event *group_leader)
|
||||
{
|
||||
/*
|
||||
* Our group leader must be an aux event if we want to be
|
||||
* an aux_output. This way, the aux event will precede its
|
||||
* aux_output events in the group, and therefore will always
|
||||
* schedule first.
|
||||
*/
|
||||
if (!group_leader)
|
||||
return 0;
|
||||
|
||||
if (!perf_aux_output_match(event, group_leader))
|
||||
return 0;
|
||||
|
||||
if (!atomic_long_inc_not_zero(&group_leader->refcount))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Link aux_outputs to their aux event; this is undone in
|
||||
* perf_group_detach() by perf_put_aux_event(). When the
|
||||
* group in torn down, the aux_output events loose their
|
||||
* link to the aux_event and can't schedule any more.
|
||||
*/
|
||||
event->aux_event = group_leader;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void perf_group_detach(struct perf_event *event)
|
||||
{
|
||||
struct perf_event *sibling, *tmp;
|
||||
|
|
@ -1907,6 +1990,8 @@ static void perf_group_detach(struct perf_event *event)
|
|||
|
||||
event->attach_state &= ~PERF_ATTACH_GROUP;
|
||||
|
||||
perf_put_aux_event(event);
|
||||
|
||||
/*
|
||||
* If this is a sibling, remove it from its group.
|
||||
*/
|
||||
|
|
@ -4094,10 +4179,8 @@ alloc_perf_context(struct pmu *pmu, struct task_struct *task)
|
|||
return NULL;
|
||||
|
||||
__perf_event_init_context(ctx);
|
||||
if (task) {
|
||||
ctx->task = task;
|
||||
get_task_struct(task);
|
||||
}
|
||||
if (task)
|
||||
ctx->task = get_task_struct(task);
|
||||
ctx->pmu = pmu;
|
||||
|
||||
return ctx;
|
||||
|
|
@ -9496,7 +9579,7 @@ static void perf_swevent_start_hrtimer(struct perf_event *event)
|
|||
period = max_t(u64, 10000, hwc->sample_period);
|
||||
}
|
||||
hrtimer_start(&hwc->hrtimer, ns_to_ktime(period),
|
||||
HRTIMER_MODE_REL_PINNED);
|
||||
HRTIMER_MODE_REL_PINNED_HARD);
|
||||
}
|
||||
|
||||
static void perf_swevent_cancel_hrtimer(struct perf_event *event)
|
||||
|
|
@ -9518,7 +9601,7 @@ static void perf_swevent_init_hrtimer(struct perf_event *event)
|
|||
if (!is_sampling_event(event))
|
||||
return;
|
||||
|
||||
hrtimer_init(&hwc->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
hrtimer_init(&hwc->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_HARD);
|
||||
hwc->hrtimer.function = perf_swevent_hrtimer;
|
||||
|
||||
/*
|
||||
|
|
@ -10360,8 +10443,7 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu,
|
|||
* and we cannot use the ctx information because we need the
|
||||
* pmu before we get a ctx.
|
||||
*/
|
||||
get_task_struct(task);
|
||||
event->hw.target = task;
|
||||
event->hw.target = get_task_struct(task);
|
||||
}
|
||||
|
||||
event->clock = &local_clock;
|
||||
|
|
@ -10431,6 +10513,12 @@ perf_event_alloc(struct perf_event_attr *attr, int cpu,
|
|||
goto err_ns;
|
||||
}
|
||||
|
||||
if (event->attr.aux_output &&
|
||||
!(pmu->capabilities & PERF_PMU_CAP_AUX_OUTPUT)) {
|
||||
err = -EOPNOTSUPP;
|
||||
goto err_pmu;
|
||||
}
|
||||
|
||||
err = exclusive_event_init(event);
|
||||
if (err)
|
||||
goto err_pmu;
|
||||
|
|
@ -11090,6 +11178,8 @@ SYSCALL_DEFINE5(perf_event_open,
|
|||
}
|
||||
}
|
||||
|
||||
if (event->attr.aux_output && !perf_get_aux_event(event, group_leader))
|
||||
goto err_locked;
|
||||
|
||||
/*
|
||||
* Must be under the same ctx::mutex as perf_install_in_context(),
|
||||
|
|
|
|||
|
|
@ -1554,6 +1554,23 @@ end:
|
|||
return retval;
|
||||
}
|
||||
|
||||
static struct pid *pidfd_get_pid(unsigned int fd)
|
||||
{
|
||||
struct fd f;
|
||||
struct pid *pid;
|
||||
|
||||
f = fdget(fd);
|
||||
if (!f.file)
|
||||
return ERR_PTR(-EBADF);
|
||||
|
||||
pid = pidfd_pid(f.file);
|
||||
if (!IS_ERR(pid))
|
||||
get_pid(pid);
|
||||
|
||||
fdput(f);
|
||||
return pid;
|
||||
}
|
||||
|
||||
static long kernel_waitid(int which, pid_t upid, struct waitid_info *infop,
|
||||
int options, struct rusage *ru)
|
||||
{
|
||||
|
|
@ -1576,19 +1593,32 @@ static long kernel_waitid(int which, pid_t upid, struct waitid_info *infop,
|
|||
type = PIDTYPE_PID;
|
||||
if (upid <= 0)
|
||||
return -EINVAL;
|
||||
|
||||
pid = find_get_pid(upid);
|
||||
break;
|
||||
case P_PGID:
|
||||
type = PIDTYPE_PGID;
|
||||
if (upid <= 0)
|
||||
if (upid < 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (upid)
|
||||
pid = find_get_pid(upid);
|
||||
else
|
||||
pid = get_task_pid(current, PIDTYPE_PGID);
|
||||
break;
|
||||
case P_PIDFD:
|
||||
type = PIDTYPE_PID;
|
||||
if (upid < 0)
|
||||
return -EINVAL;
|
||||
|
||||
pid = pidfd_get_pid(upid);
|
||||
if (IS_ERR(pid))
|
||||
return PTR_ERR(pid);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (type < PIDTYPE_MAX)
|
||||
pid = find_get_pid(upid);
|
||||
|
||||
wo.wo_type = type;
|
||||
wo.wo_pid = pid;
|
||||
wo.wo_flags = options;
|
||||
|
|
|
|||
|
|
@ -771,6 +771,7 @@ static void set_max_threads(unsigned int max_threads_suggested)
|
|||
int arch_task_struct_size __read_mostly;
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_ARCH_TASK_STRUCT_ALLOCATOR
|
||||
static void task_struct_whitelist(unsigned long *offset, unsigned long *size)
|
||||
{
|
||||
/* Fetch thread_struct whitelist for the architecture. */
|
||||
|
|
@ -785,6 +786,7 @@ static void task_struct_whitelist(unsigned long *offset, unsigned long *size)
|
|||
else
|
||||
*offset += offsetof(struct task_struct, thread);
|
||||
}
|
||||
#endif /* CONFIG_ARCH_TASK_STRUCT_ALLOCATOR */
|
||||
|
||||
void __init fork_init(void)
|
||||
{
|
||||
|
|
@ -1520,28 +1522,17 @@ void __cleanup_sighand(struct sighand_struct *sighand)
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_POSIX_TIMERS
|
||||
/*
|
||||
* Initialize POSIX timer handling for a thread group.
|
||||
*/
|
||||
static void posix_cpu_timers_init_group(struct signal_struct *sig)
|
||||
{
|
||||
struct posix_cputimers *pct = &sig->posix_cputimers;
|
||||
unsigned long cpu_limit;
|
||||
|
||||
cpu_limit = READ_ONCE(sig->rlim[RLIMIT_CPU].rlim_cur);
|
||||
if (cpu_limit != RLIM_INFINITY) {
|
||||
sig->cputime_expires.prof_exp = cpu_limit * NSEC_PER_SEC;
|
||||
sig->cputimer.running = true;
|
||||
}
|
||||
|
||||
/* The timer lists. */
|
||||
INIT_LIST_HEAD(&sig->cpu_timers[0]);
|
||||
INIT_LIST_HEAD(&sig->cpu_timers[1]);
|
||||
INIT_LIST_HEAD(&sig->cpu_timers[2]);
|
||||
posix_cputimers_group_init(pct, cpu_limit);
|
||||
}
|
||||
#else
|
||||
static inline void posix_cpu_timers_init_group(struct signal_struct *sig) { }
|
||||
#endif
|
||||
|
||||
static int copy_signal(unsigned long clone_flags, struct task_struct *tsk)
|
||||
{
|
||||
|
|
@ -1643,23 +1634,6 @@ static void rt_mutex_init_task(struct task_struct *p)
|
|||
#endif
|
||||
}
|
||||
|
||||
#ifdef CONFIG_POSIX_TIMERS
|
||||
/*
|
||||
* Initialize POSIX timer handling for a single task.
|
||||
*/
|
||||
static void posix_cpu_timers_init(struct task_struct *tsk)
|
||||
{
|
||||
tsk->cputime_expires.prof_exp = 0;
|
||||
tsk->cputime_expires.virt_exp = 0;
|
||||
tsk->cputime_expires.sched_exp = 0;
|
||||
INIT_LIST_HEAD(&tsk->cpu_timers[0]);
|
||||
INIT_LIST_HEAD(&tsk->cpu_timers[1]);
|
||||
INIT_LIST_HEAD(&tsk->cpu_timers[2]);
|
||||
}
|
||||
#else
|
||||
static inline void posix_cpu_timers_init(struct task_struct *tsk) { }
|
||||
#endif
|
||||
|
||||
static inline void init_task_pid_links(struct task_struct *task)
|
||||
{
|
||||
enum pid_type type;
|
||||
|
|
@ -1693,6 +1667,14 @@ static inline void rcu_copy_process(struct task_struct *p)
|
|||
#endif /* #ifdef CONFIG_TASKS_RCU */
|
||||
}
|
||||
|
||||
struct pid *pidfd_pid(const struct file *file)
|
||||
{
|
||||
if (file->f_op == &pidfd_fops)
|
||||
return file->private_data;
|
||||
|
||||
return ERR_PTR(-EBADF);
|
||||
}
|
||||
|
||||
static int pidfd_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct pid *pid = file->private_data;
|
||||
|
|
@ -1940,7 +1922,7 @@ static __latent_entropy struct task_struct *copy_process(
|
|||
task_io_accounting_init(&p->ioac);
|
||||
acct_clear_integrals(p);
|
||||
|
||||
posix_cpu_timers_init(p);
|
||||
posix_cputimers_init(&p->posix_cputimers);
|
||||
|
||||
p->io_context = NULL;
|
||||
audit_set_context(p, NULL);
|
||||
|
|
|
|||
|
|
@ -487,11 +487,9 @@ futex_setup_timer(ktime_t *time, struct hrtimer_sleeper *timeout,
|
|||
if (!time)
|
||||
return NULL;
|
||||
|
||||
hrtimer_init_on_stack(&timeout->timer, (flags & FLAGS_CLOCKRT) ?
|
||||
CLOCK_REALTIME : CLOCK_MONOTONIC,
|
||||
HRTIMER_MODE_ABS);
|
||||
hrtimer_init_sleeper(timeout, current);
|
||||
|
||||
hrtimer_init_sleeper_on_stack(timeout, (flags & FLAGS_CLOCKRT) ?
|
||||
CLOCK_REALTIME : CLOCK_MONOTONIC,
|
||||
HRTIMER_MODE_ABS);
|
||||
/*
|
||||
* If range_ns is 0, calling hrtimer_set_expires_range_ns() is
|
||||
* effectively the same as calling hrtimer_set_expires().
|
||||
|
|
@ -2613,7 +2611,7 @@ static void futex_wait_queue_me(struct futex_hash_bucket *hb, struct futex_q *q,
|
|||
|
||||
/* Arm the timer */
|
||||
if (timeout)
|
||||
hrtimer_start_expires(&timeout->timer, HRTIMER_MODE_ABS);
|
||||
hrtimer_sleeper_start_expires(timeout, HRTIMER_MODE_ABS);
|
||||
|
||||
/*
|
||||
* If we have been removed from the hash list, then another task
|
||||
|
|
@ -2899,7 +2897,7 @@ retry_private:
|
|||
}
|
||||
|
||||
if (unlikely(to))
|
||||
hrtimer_start_expires(&to->timer, HRTIMER_MODE_ABS);
|
||||
hrtimer_sleeper_start_expires(to, HRTIMER_MODE_ABS);
|
||||
|
||||
ret = rt_mutex_wait_proxy_lock(&q.pi_state->pi_mutex, to, &rt_waiter);
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/sort.h>
|
||||
|
||||
static void irq_spread_init_one(struct cpumask *irqmsk, struct cpumask *nmsk,
|
||||
unsigned int cpus_per_vec)
|
||||
|
|
@ -94,6 +95,155 @@ static int get_nodes_in_cpumask(cpumask_var_t *node_to_cpumask,
|
|||
return nodes;
|
||||
}
|
||||
|
||||
struct node_vectors {
|
||||
unsigned id;
|
||||
|
||||
union {
|
||||
unsigned nvectors;
|
||||
unsigned ncpus;
|
||||
};
|
||||
};
|
||||
|
||||
static int ncpus_cmp_func(const void *l, const void *r)
|
||||
{
|
||||
const struct node_vectors *ln = l;
|
||||
const struct node_vectors *rn = r;
|
||||
|
||||
return ln->ncpus - rn->ncpus;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate vector number for each node, so that for each node:
|
||||
*
|
||||
* 1) the allocated number is >= 1
|
||||
*
|
||||
* 2) the allocated numbver is <= active CPU number of this node
|
||||
*
|
||||
* The actual allocated total vectors may be less than @numvecs when
|
||||
* active total CPU number is less than @numvecs.
|
||||
*
|
||||
* Active CPUs means the CPUs in '@cpu_mask AND @node_to_cpumask[]'
|
||||
* for each node.
|
||||
*/
|
||||
static void alloc_nodes_vectors(unsigned int numvecs,
|
||||
cpumask_var_t *node_to_cpumask,
|
||||
const struct cpumask *cpu_mask,
|
||||
const nodemask_t nodemsk,
|
||||
struct cpumask *nmsk,
|
||||
struct node_vectors *node_vectors)
|
||||
{
|
||||
unsigned n, remaining_ncpus = 0;
|
||||
|
||||
for (n = 0; n < nr_node_ids; n++) {
|
||||
node_vectors[n].id = n;
|
||||
node_vectors[n].ncpus = UINT_MAX;
|
||||
}
|
||||
|
||||
for_each_node_mask(n, nodemsk) {
|
||||
unsigned ncpus;
|
||||
|
||||
cpumask_and(nmsk, cpu_mask, node_to_cpumask[n]);
|
||||
ncpus = cpumask_weight(nmsk);
|
||||
|
||||
if (!ncpus)
|
||||
continue;
|
||||
remaining_ncpus += ncpus;
|
||||
node_vectors[n].ncpus = ncpus;
|
||||
}
|
||||
|
||||
numvecs = min_t(unsigned, remaining_ncpus, numvecs);
|
||||
|
||||
sort(node_vectors, nr_node_ids, sizeof(node_vectors[0]),
|
||||
ncpus_cmp_func, NULL);
|
||||
|
||||
/*
|
||||
* Allocate vectors for each node according to the ratio of this
|
||||
* node's nr_cpus to remaining un-assigned ncpus. 'numvecs' is
|
||||
* bigger than number of active numa nodes. Always start the
|
||||
* allocation from the node with minimized nr_cpus.
|
||||
*
|
||||
* This way guarantees that each active node gets allocated at
|
||||
* least one vector, and the theory is simple: over-allocation
|
||||
* is only done when this node is assigned by one vector, so
|
||||
* other nodes will be allocated >= 1 vector, since 'numvecs' is
|
||||
* bigger than number of numa nodes.
|
||||
*
|
||||
* One perfect invariant is that number of allocated vectors for
|
||||
* each node is <= CPU count of this node:
|
||||
*
|
||||
* 1) suppose there are two nodes: A and B
|
||||
* ncpu(X) is CPU count of node X
|
||||
* vecs(X) is the vector count allocated to node X via this
|
||||
* algorithm
|
||||
*
|
||||
* ncpu(A) <= ncpu(B)
|
||||
* ncpu(A) + ncpu(B) = N
|
||||
* vecs(A) + vecs(B) = V
|
||||
*
|
||||
* vecs(A) = max(1, round_down(V * ncpu(A) / N))
|
||||
* vecs(B) = V - vecs(A)
|
||||
*
|
||||
* both N and V are integer, and 2 <= V <= N, suppose
|
||||
* V = N - delta, and 0 <= delta <= N - 2
|
||||
*
|
||||
* 2) obviously vecs(A) <= ncpu(A) because:
|
||||
*
|
||||
* if vecs(A) is 1, then vecs(A) <= ncpu(A) given
|
||||
* ncpu(A) >= 1
|
||||
*
|
||||
* otherwise,
|
||||
* vecs(A) <= V * ncpu(A) / N <= ncpu(A), given V <= N
|
||||
*
|
||||
* 3) prove how vecs(B) <= ncpu(B):
|
||||
*
|
||||
* if round_down(V * ncpu(A) / N) == 0, vecs(B) won't be
|
||||
* over-allocated, so vecs(B) <= ncpu(B),
|
||||
*
|
||||
* otherwise:
|
||||
*
|
||||
* vecs(A) =
|
||||
* round_down(V * ncpu(A) / N) =
|
||||
* round_down((N - delta) * ncpu(A) / N) =
|
||||
* round_down((N * ncpu(A) - delta * ncpu(A)) / N) >=
|
||||
* round_down((N * ncpu(A) - delta * N) / N) =
|
||||
* cpu(A) - delta
|
||||
*
|
||||
* then:
|
||||
*
|
||||
* vecs(A) - V >= ncpu(A) - delta - V
|
||||
* =>
|
||||
* V - vecs(A) <= V + delta - ncpu(A)
|
||||
* =>
|
||||
* vecs(B) <= N - ncpu(A)
|
||||
* =>
|
||||
* vecs(B) <= cpu(B)
|
||||
*
|
||||
* For nodes >= 3, it can be thought as one node and another big
|
||||
* node given that is exactly what this algorithm is implemented,
|
||||
* and we always re-calculate 'remaining_ncpus' & 'numvecs', and
|
||||
* finally for each node X: vecs(X) <= ncpu(X).
|
||||
*
|
||||
*/
|
||||
for (n = 0; n < nr_node_ids; n++) {
|
||||
unsigned nvectors, ncpus;
|
||||
|
||||
if (node_vectors[n].ncpus == UINT_MAX)
|
||||
continue;
|
||||
|
||||
WARN_ON_ONCE(numvecs == 0);
|
||||
|
||||
ncpus = node_vectors[n].ncpus;
|
||||
nvectors = max_t(unsigned, 1,
|
||||
numvecs * ncpus / remaining_ncpus);
|
||||
WARN_ON_ONCE(nvectors > ncpus);
|
||||
|
||||
node_vectors[n].nvectors = nvectors;
|
||||
|
||||
remaining_ncpus -= ncpus;
|
||||
numvecs -= nvectors;
|
||||
}
|
||||
}
|
||||
|
||||
static int __irq_build_affinity_masks(unsigned int startvec,
|
||||
unsigned int numvecs,
|
||||
unsigned int firstvec,
|
||||
|
|
@ -102,10 +252,11 @@ static int __irq_build_affinity_masks(unsigned int startvec,
|
|||
struct cpumask *nmsk,
|
||||
struct irq_affinity_desc *masks)
|
||||
{
|
||||
unsigned int n, nodes, cpus_per_vec, extra_vecs, done = 0;
|
||||
unsigned int i, n, nodes, cpus_per_vec, extra_vecs, done = 0;
|
||||
unsigned int last_affv = firstvec + numvecs;
|
||||
unsigned int curvec = startvec;
|
||||
nodemask_t nodemsk = NODE_MASK_NONE;
|
||||
struct node_vectors *node_vectors;
|
||||
|
||||
if (!cpumask_weight(cpu_mask))
|
||||
return 0;
|
||||
|
|
@ -126,42 +277,56 @@ static int __irq_build_affinity_masks(unsigned int startvec,
|
|||
return numvecs;
|
||||
}
|
||||
|
||||
for_each_node_mask(n, nodemsk) {
|
||||
unsigned int ncpus, v, vecs_to_assign, vecs_per_node;
|
||||
node_vectors = kcalloc(nr_node_ids,
|
||||
sizeof(struct node_vectors),
|
||||
GFP_KERNEL);
|
||||
if (!node_vectors)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Spread the vectors per node */
|
||||
vecs_per_node = (numvecs - (curvec - firstvec)) / nodes;
|
||||
/* allocate vector number for each node */
|
||||
alloc_nodes_vectors(numvecs, node_to_cpumask, cpu_mask,
|
||||
nodemsk, nmsk, node_vectors);
|
||||
|
||||
for (i = 0; i < nr_node_ids; i++) {
|
||||
unsigned int ncpus, v;
|
||||
struct node_vectors *nv = &node_vectors[i];
|
||||
|
||||
if (nv->nvectors == UINT_MAX)
|
||||
continue;
|
||||
|
||||
/* Get the cpus on this node which are in the mask */
|
||||
cpumask_and(nmsk, cpu_mask, node_to_cpumask[n]);
|
||||
|
||||
/* Calculate the number of cpus per vector */
|
||||
cpumask_and(nmsk, cpu_mask, node_to_cpumask[nv->id]);
|
||||
ncpus = cpumask_weight(nmsk);
|
||||
vecs_to_assign = min(vecs_per_node, ncpus);
|
||||
if (!ncpus)
|
||||
continue;
|
||||
|
||||
WARN_ON_ONCE(nv->nvectors > ncpus);
|
||||
|
||||
/* Account for rounding errors */
|
||||
extra_vecs = ncpus - vecs_to_assign * (ncpus / vecs_to_assign);
|
||||
extra_vecs = ncpus - nv->nvectors * (ncpus / nv->nvectors);
|
||||
|
||||
for (v = 0; curvec < last_affv && v < vecs_to_assign;
|
||||
curvec++, v++) {
|
||||
cpus_per_vec = ncpus / vecs_to_assign;
|
||||
/* Spread allocated vectors on CPUs of the current node */
|
||||
for (v = 0; v < nv->nvectors; v++, curvec++) {
|
||||
cpus_per_vec = ncpus / nv->nvectors;
|
||||
|
||||
/* Account for extra vectors to compensate rounding errors */
|
||||
if (extra_vecs) {
|
||||
cpus_per_vec++;
|
||||
--extra_vecs;
|
||||
}
|
||||
|
||||
/*
|
||||
* wrapping has to be considered given 'startvec'
|
||||
* may start anywhere
|
||||
*/
|
||||
if (curvec >= last_affv)
|
||||
curvec = firstvec;
|
||||
irq_spread_init_one(&masks[curvec].mask, nmsk,
|
||||
cpus_per_vec);
|
||||
}
|
||||
|
||||
done += v;
|
||||
if (done >= numvecs)
|
||||
break;
|
||||
if (curvec >= last_affv)
|
||||
curvec = firstvec;
|
||||
--nodes;
|
||||
done += nv->nvectors;
|
||||
}
|
||||
kfree(node_vectors);
|
||||
return done;
|
||||
}
|
||||
|
||||
|
|
@ -174,7 +339,7 @@ static int irq_build_affinity_masks(unsigned int startvec, unsigned int numvecs,
|
|||
unsigned int firstvec,
|
||||
struct irq_affinity_desc *masks)
|
||||
{
|
||||
unsigned int curvec = startvec, nr_present, nr_others;
|
||||
unsigned int curvec = startvec, nr_present = 0, nr_others = 0;
|
||||
cpumask_var_t *node_to_cpumask;
|
||||
cpumask_var_t nmsk, npresmsk;
|
||||
int ret = -ENOMEM;
|
||||
|
|
@ -189,15 +354,17 @@ static int irq_build_affinity_masks(unsigned int startvec, unsigned int numvecs,
|
|||
if (!node_to_cpumask)
|
||||
goto fail_npresmsk;
|
||||
|
||||
ret = 0;
|
||||
/* Stabilize the cpumasks */
|
||||
get_online_cpus();
|
||||
build_node_to_cpumask(node_to_cpumask);
|
||||
|
||||
/* Spread on present CPUs starting from affd->pre_vectors */
|
||||
nr_present = __irq_build_affinity_masks(curvec, numvecs,
|
||||
firstvec, node_to_cpumask,
|
||||
cpu_present_mask, nmsk, masks);
|
||||
ret = __irq_build_affinity_masks(curvec, numvecs, firstvec,
|
||||
node_to_cpumask, cpu_present_mask,
|
||||
nmsk, masks);
|
||||
if (ret < 0)
|
||||
goto fail_build_affinity;
|
||||
nr_present = ret;
|
||||
|
||||
/*
|
||||
* Spread on non present CPUs starting from the next vector to be
|
||||
|
|
@ -210,12 +377,16 @@ static int irq_build_affinity_masks(unsigned int startvec, unsigned int numvecs,
|
|||
else
|
||||
curvec = firstvec + nr_present;
|
||||
cpumask_andnot(npresmsk, cpu_possible_mask, cpu_present_mask);
|
||||
nr_others = __irq_build_affinity_masks(curvec, numvecs,
|
||||
firstvec, node_to_cpumask,
|
||||
npresmsk, nmsk, masks);
|
||||
ret = __irq_build_affinity_masks(curvec, numvecs, firstvec,
|
||||
node_to_cpumask, npresmsk, nmsk,
|
||||
masks);
|
||||
if (ret >= 0)
|
||||
nr_others = ret;
|
||||
|
||||
fail_build_affinity:
|
||||
put_online_cpus();
|
||||
|
||||
if (nr_present < numvecs)
|
||||
if (ret >= 0)
|
||||
WARN_ON(nr_present + nr_others < numvecs);
|
||||
|
||||
free_node_to_cpumask(node_to_cpumask);
|
||||
|
|
@ -225,7 +396,7 @@ static int irq_build_affinity_masks(unsigned int startvec, unsigned int numvecs,
|
|||
|
||||
fail_nmsk:
|
||||
free_cpumask_var(nmsk);
|
||||
return ret;
|
||||
return ret < 0 ? ret : 0;
|
||||
}
|
||||
|
||||
static void default_calc_sets(struct irq_affinity *affd, unsigned int affvecs)
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ struct irqchip_fwid {
|
|||
struct fwnode_handle fwnode;
|
||||
unsigned int type;
|
||||
char *name;
|
||||
void *data;
|
||||
phys_addr_t *pa;
|
||||
};
|
||||
|
||||
#ifdef CONFIG_GENERIC_IRQ_DEBUGFS
|
||||
|
|
@ -62,7 +62,8 @@ EXPORT_SYMBOL_GPL(irqchip_fwnode_ops);
|
|||
* domain struct.
|
||||
*/
|
||||
struct fwnode_handle *__irq_domain_alloc_fwnode(unsigned int type, int id,
|
||||
const char *name, void *data)
|
||||
const char *name,
|
||||
phys_addr_t *pa)
|
||||
{
|
||||
struct irqchip_fwid *fwid;
|
||||
char *n;
|
||||
|
|
@ -77,7 +78,7 @@ struct fwnode_handle *__irq_domain_alloc_fwnode(unsigned int type, int id,
|
|||
n = kasprintf(GFP_KERNEL, "%s-%d", name, id);
|
||||
break;
|
||||
default:
|
||||
n = kasprintf(GFP_KERNEL, "irqchip@%p", data);
|
||||
n = kasprintf(GFP_KERNEL, "irqchip@%pa", pa);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -89,7 +90,7 @@ struct fwnode_handle *__irq_domain_alloc_fwnode(unsigned int type, int id,
|
|||
|
||||
fwid->type = type;
|
||||
fwid->name = n;
|
||||
fwid->data = data;
|
||||
fwid->pa = pa;
|
||||
fwid->fwnode.ops = &irqchip_fwnode_ops;
|
||||
return &fwid->fwnode;
|
||||
}
|
||||
|
|
@ -148,6 +149,7 @@ struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, int size,
|
|||
switch (fwid->type) {
|
||||
case IRQCHIP_FWNODE_NAMED:
|
||||
case IRQCHIP_FWNODE_NAMED_ID:
|
||||
domain->fwnode = fwnode;
|
||||
domain->name = kstrdup(fwid->name, GFP_KERNEL);
|
||||
if (!domain->name) {
|
||||
kfree(domain);
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
#include "internals.h"
|
||||
|
||||
#ifdef CONFIG_IRQ_FORCED_THREADING
|
||||
#if defined(CONFIG_IRQ_FORCED_THREADING) && !defined(CONFIG_PREEMPT_RT)
|
||||
__read_mostly bool force_irqthreads;
|
||||
EXPORT_SYMBOL_GPL(force_irqthreads);
|
||||
|
||||
|
|
@ -1255,8 +1255,7 @@ setup_irq_thread(struct irqaction *new, unsigned int irq, bool secondary)
|
|||
* the thread dies to avoid that the interrupt code
|
||||
* references an already freed task_struct.
|
||||
*/
|
||||
get_task_struct(t);
|
||||
new->thread = t;
|
||||
new->thread = get_task_struct(t);
|
||||
/*
|
||||
* Tell the thread to set its affinity. This is
|
||||
* important for shared interrupt handlers as we do
|
||||
|
|
|
|||
|
|
@ -176,6 +176,26 @@ static void resume_irqs(bool want_early)
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* rearm_wake_irq - rearm a wakeup interrupt line after signaling wakeup
|
||||
* @irq: Interrupt to rearm
|
||||
*/
|
||||
void rearm_wake_irq(unsigned int irq)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL);
|
||||
|
||||
if (!desc || !(desc->istate & IRQS_SUSPENDED) ||
|
||||
!irqd_is_wakeup_set(&desc->irq_data))
|
||||
return;
|
||||
|
||||
desc->istate &= ~IRQS_SUSPENDED;
|
||||
irqd_set(&desc->irq_data, IRQD_WAKEUP_ARMED);
|
||||
__enable_irq(desc);
|
||||
|
||||
irq_put_desc_busunlock(desc, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* irq_pm_syscore_ops - enable interrupt lines early
|
||||
*
|
||||
|
|
|
|||
|
|
@ -100,10 +100,6 @@ static int irq_affinity_hint_proc_show(struct seq_file *m, void *v)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifndef is_affinity_mask_valid
|
||||
#define is_affinity_mask_valid(val) 1
|
||||
#endif
|
||||
|
||||
int no_irq_affinity;
|
||||
static int irq_affinity_proc_show(struct seq_file *m, void *v)
|
||||
{
|
||||
|
|
@ -136,11 +132,6 @@ static ssize_t write_irq_affinity(int type, struct file *file,
|
|||
if (err)
|
||||
goto free_cpumask;
|
||||
|
||||
if (!is_affinity_mask_valid(new_value)) {
|
||||
err = -EINVAL;
|
||||
goto free_cpumask;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do not allow disabling IRQs completely - it's a too easy
|
||||
* way to make the system unusable accidentally :-) At least
|
||||
|
|
@ -232,11 +223,6 @@ static ssize_t default_affinity_write(struct file *file,
|
|||
if (err)
|
||||
goto out;
|
||||
|
||||
if (!is_affinity_mask_valid(new_value)) {
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do not allow disabling IRQs completely - it's a too easy
|
||||
* way to make the system unusable accidentally :-) At least
|
||||
|
|
|
|||
|
|
@ -407,7 +407,9 @@ static bool jump_label_can_update(struct jump_entry *entry, bool init)
|
|||
return false;
|
||||
|
||||
if (!kernel_text_address(jump_entry_code(entry))) {
|
||||
WARN_ONCE(1, "can't patch jump_label at %pS", (void *)jump_entry_code(entry));
|
||||
WARN_ONCE(!jump_entry_is_init(entry),
|
||||
"can't patch jump_label at %pS",
|
||||
(void *)jump_entry_code(entry));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
430
kernel/kexec_elf.c
Normal file
430
kernel/kexec_elf.c
Normal file
|
|
@ -0,0 +1,430 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Load ELF vmlinux file for the kexec_file_load syscall.
|
||||
*
|
||||
* Copyright (C) 2004 Adam Litke (agl@us.ibm.com)
|
||||
* Copyright (C) 2004 IBM Corp.
|
||||
* Copyright (C) 2005 R Sharada (sharada@in.ibm.com)
|
||||
* Copyright (C) 2006 Mohan Kumar M (mohan@in.ibm.com)
|
||||
* Copyright (C) 2016 IBM Corporation
|
||||
*
|
||||
* Based on kexec-tools' kexec-elf-exec.c and kexec-elf-ppc64.c.
|
||||
* Heavily modified for the kernel by
|
||||
* Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "kexec_elf: " fmt
|
||||
|
||||
#include <linux/elf.h>
|
||||
#include <linux/kexec.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
static inline bool elf_is_elf_file(const struct elfhdr *ehdr)
|
||||
{
|
||||
return memcmp(ehdr->e_ident, ELFMAG, SELFMAG) == 0;
|
||||
}
|
||||
|
||||
static uint64_t elf64_to_cpu(const struct elfhdr *ehdr, uint64_t value)
|
||||
{
|
||||
if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
|
||||
value = le64_to_cpu(value);
|
||||
else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
|
||||
value = be64_to_cpu(value);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static uint32_t elf32_to_cpu(const struct elfhdr *ehdr, uint32_t value)
|
||||
{
|
||||
if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
|
||||
value = le32_to_cpu(value);
|
||||
else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
|
||||
value = be32_to_cpu(value);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static uint16_t elf16_to_cpu(const struct elfhdr *ehdr, uint16_t value)
|
||||
{
|
||||
if (ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
|
||||
value = le16_to_cpu(value);
|
||||
else if (ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
|
||||
value = be16_to_cpu(value);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* elf_is_ehdr_sane - check that it is safe to use the ELF header
|
||||
* @buf_len: size of the buffer in which the ELF file is loaded.
|
||||
*/
|
||||
static bool elf_is_ehdr_sane(const struct elfhdr *ehdr, size_t buf_len)
|
||||
{
|
||||
if (ehdr->e_phnum > 0 && ehdr->e_phentsize != sizeof(struct elf_phdr)) {
|
||||
pr_debug("Bad program header size.\n");
|
||||
return false;
|
||||
} else if (ehdr->e_shnum > 0 &&
|
||||
ehdr->e_shentsize != sizeof(struct elf_shdr)) {
|
||||
pr_debug("Bad section header size.\n");
|
||||
return false;
|
||||
} else if (ehdr->e_ident[EI_VERSION] != EV_CURRENT ||
|
||||
ehdr->e_version != EV_CURRENT) {
|
||||
pr_debug("Unknown ELF version.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ehdr->e_phoff > 0 && ehdr->e_phnum > 0) {
|
||||
size_t phdr_size;
|
||||
|
||||
/*
|
||||
* e_phnum is at most 65535 so calculating the size of the
|
||||
* program header cannot overflow.
|
||||
*/
|
||||
phdr_size = sizeof(struct elf_phdr) * ehdr->e_phnum;
|
||||
|
||||
/* Sanity check the program header table location. */
|
||||
if (ehdr->e_phoff + phdr_size < ehdr->e_phoff) {
|
||||
pr_debug("Program headers at invalid location.\n");
|
||||
return false;
|
||||
} else if (ehdr->e_phoff + phdr_size > buf_len) {
|
||||
pr_debug("Program headers truncated.\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (ehdr->e_shoff > 0 && ehdr->e_shnum > 0) {
|
||||
size_t shdr_size;
|
||||
|
||||
/*
|
||||
* e_shnum is at most 65536 so calculating
|
||||
* the size of the section header cannot overflow.
|
||||
*/
|
||||
shdr_size = sizeof(struct elf_shdr) * ehdr->e_shnum;
|
||||
|
||||
/* Sanity check the section header table location. */
|
||||
if (ehdr->e_shoff + shdr_size < ehdr->e_shoff) {
|
||||
pr_debug("Section headers at invalid location.\n");
|
||||
return false;
|
||||
} else if (ehdr->e_shoff + shdr_size > buf_len) {
|
||||
pr_debug("Section headers truncated.\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int elf_read_ehdr(const char *buf, size_t len, struct elfhdr *ehdr)
|
||||
{
|
||||
struct elfhdr *buf_ehdr;
|
||||
|
||||
if (len < sizeof(*buf_ehdr)) {
|
||||
pr_debug("Buffer is too small to hold ELF header.\n");
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
memset(ehdr, 0, sizeof(*ehdr));
|
||||
memcpy(ehdr->e_ident, buf, sizeof(ehdr->e_ident));
|
||||
if (!elf_is_elf_file(ehdr)) {
|
||||
pr_debug("No ELF header magic.\n");
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
if (ehdr->e_ident[EI_CLASS] != ELF_CLASS) {
|
||||
pr_debug("Not a supported ELF class.\n");
|
||||
return -ENOEXEC;
|
||||
} else if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB &&
|
||||
ehdr->e_ident[EI_DATA] != ELFDATA2MSB) {
|
||||
pr_debug("Not a supported ELF data format.\n");
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
buf_ehdr = (struct elfhdr *) buf;
|
||||
if (elf16_to_cpu(ehdr, buf_ehdr->e_ehsize) != sizeof(*buf_ehdr)) {
|
||||
pr_debug("Bad ELF header size.\n");
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
ehdr->e_type = elf16_to_cpu(ehdr, buf_ehdr->e_type);
|
||||
ehdr->e_machine = elf16_to_cpu(ehdr, buf_ehdr->e_machine);
|
||||
ehdr->e_version = elf32_to_cpu(ehdr, buf_ehdr->e_version);
|
||||
ehdr->e_flags = elf32_to_cpu(ehdr, buf_ehdr->e_flags);
|
||||
ehdr->e_phentsize = elf16_to_cpu(ehdr, buf_ehdr->e_phentsize);
|
||||
ehdr->e_phnum = elf16_to_cpu(ehdr, buf_ehdr->e_phnum);
|
||||
ehdr->e_shentsize = elf16_to_cpu(ehdr, buf_ehdr->e_shentsize);
|
||||
ehdr->e_shnum = elf16_to_cpu(ehdr, buf_ehdr->e_shnum);
|
||||
ehdr->e_shstrndx = elf16_to_cpu(ehdr, buf_ehdr->e_shstrndx);
|
||||
|
||||
switch (ehdr->e_ident[EI_CLASS]) {
|
||||
case ELFCLASS64:
|
||||
ehdr->e_entry = elf64_to_cpu(ehdr, buf_ehdr->e_entry);
|
||||
ehdr->e_phoff = elf64_to_cpu(ehdr, buf_ehdr->e_phoff);
|
||||
ehdr->e_shoff = elf64_to_cpu(ehdr, buf_ehdr->e_shoff);
|
||||
break;
|
||||
|
||||
case ELFCLASS32:
|
||||
ehdr->e_entry = elf32_to_cpu(ehdr, buf_ehdr->e_entry);
|
||||
ehdr->e_phoff = elf32_to_cpu(ehdr, buf_ehdr->e_phoff);
|
||||
ehdr->e_shoff = elf32_to_cpu(ehdr, buf_ehdr->e_shoff);
|
||||
break;
|
||||
|
||||
default:
|
||||
pr_debug("Unknown ELF class.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return elf_is_ehdr_sane(ehdr, len) ? 0 : -ENOEXEC;
|
||||
}
|
||||
|
||||
/**
|
||||
* elf_is_phdr_sane - check that it is safe to use the program header
|
||||
* @buf_len: size of the buffer in which the ELF file is loaded.
|
||||
*/
|
||||
static bool elf_is_phdr_sane(const struct elf_phdr *phdr, size_t buf_len)
|
||||
{
|
||||
|
||||
if (phdr->p_offset + phdr->p_filesz < phdr->p_offset) {
|
||||
pr_debug("ELF segment location wraps around.\n");
|
||||
return false;
|
||||
} else if (phdr->p_offset + phdr->p_filesz > buf_len) {
|
||||
pr_debug("ELF segment not in file.\n");
|
||||
return false;
|
||||
} else if (phdr->p_paddr + phdr->p_memsz < phdr->p_paddr) {
|
||||
pr_debug("ELF segment address wraps around.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int elf_read_phdr(const char *buf, size_t len,
|
||||
struct kexec_elf_info *elf_info,
|
||||
int idx)
|
||||
{
|
||||
/* Override the const in proghdrs, we are the ones doing the loading. */
|
||||
struct elf_phdr *phdr = (struct elf_phdr *) &elf_info->proghdrs[idx];
|
||||
const struct elfhdr *ehdr = elf_info->ehdr;
|
||||
const char *pbuf;
|
||||
struct elf_phdr *buf_phdr;
|
||||
|
||||
pbuf = buf + elf_info->ehdr->e_phoff + (idx * sizeof(*buf_phdr));
|
||||
buf_phdr = (struct elf_phdr *) pbuf;
|
||||
|
||||
phdr->p_type = elf32_to_cpu(elf_info->ehdr, buf_phdr->p_type);
|
||||
phdr->p_flags = elf32_to_cpu(elf_info->ehdr, buf_phdr->p_flags);
|
||||
|
||||
switch (ehdr->e_ident[EI_CLASS]) {
|
||||
case ELFCLASS64:
|
||||
phdr->p_offset = elf64_to_cpu(ehdr, buf_phdr->p_offset);
|
||||
phdr->p_paddr = elf64_to_cpu(ehdr, buf_phdr->p_paddr);
|
||||
phdr->p_vaddr = elf64_to_cpu(ehdr, buf_phdr->p_vaddr);
|
||||
phdr->p_filesz = elf64_to_cpu(ehdr, buf_phdr->p_filesz);
|
||||
phdr->p_memsz = elf64_to_cpu(ehdr, buf_phdr->p_memsz);
|
||||
phdr->p_align = elf64_to_cpu(ehdr, buf_phdr->p_align);
|
||||
break;
|
||||
|
||||
case ELFCLASS32:
|
||||
phdr->p_offset = elf32_to_cpu(ehdr, buf_phdr->p_offset);
|
||||
phdr->p_paddr = elf32_to_cpu(ehdr, buf_phdr->p_paddr);
|
||||
phdr->p_vaddr = elf32_to_cpu(ehdr, buf_phdr->p_vaddr);
|
||||
phdr->p_filesz = elf32_to_cpu(ehdr, buf_phdr->p_filesz);
|
||||
phdr->p_memsz = elf32_to_cpu(ehdr, buf_phdr->p_memsz);
|
||||
phdr->p_align = elf32_to_cpu(ehdr, buf_phdr->p_align);
|
||||
break;
|
||||
|
||||
default:
|
||||
pr_debug("Unknown ELF class.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return elf_is_phdr_sane(phdr, len) ? 0 : -ENOEXEC;
|
||||
}
|
||||
|
||||
/**
|
||||
* elf_read_phdrs - read the program headers from the buffer
|
||||
*
|
||||
* This function assumes that the program header table was checked for sanity.
|
||||
* Use elf_is_ehdr_sane() if it wasn't.
|
||||
*/
|
||||
static int elf_read_phdrs(const char *buf, size_t len,
|
||||
struct kexec_elf_info *elf_info)
|
||||
{
|
||||
size_t phdr_size, i;
|
||||
const struct elfhdr *ehdr = elf_info->ehdr;
|
||||
|
||||
/*
|
||||
* e_phnum is at most 65535 so calculating the size of the
|
||||
* program header cannot overflow.
|
||||
*/
|
||||
phdr_size = sizeof(struct elf_phdr) * ehdr->e_phnum;
|
||||
|
||||
elf_info->proghdrs = kzalloc(phdr_size, GFP_KERNEL);
|
||||
if (!elf_info->proghdrs)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < ehdr->e_phnum; i++) {
|
||||
int ret;
|
||||
|
||||
ret = elf_read_phdr(buf, len, elf_info, i);
|
||||
if (ret) {
|
||||
kfree(elf_info->proghdrs);
|
||||
elf_info->proghdrs = NULL;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* elf_read_from_buffer - read ELF file and sets up ELF header and ELF info
|
||||
* @buf: Buffer to read ELF file from.
|
||||
* @len: Size of @buf.
|
||||
* @ehdr: Pointer to existing struct which will be populated.
|
||||
* @elf_info: Pointer to existing struct which will be populated.
|
||||
*
|
||||
* This function allows reading ELF files with different byte order than
|
||||
* the kernel, byte-swapping the fields as needed.
|
||||
*
|
||||
* Return:
|
||||
* On success returns 0, and the caller should call
|
||||
* kexec_free_elf_info(elf_info) to free the memory allocated for the section
|
||||
* and program headers.
|
||||
*/
|
||||
static int elf_read_from_buffer(const char *buf, size_t len,
|
||||
struct elfhdr *ehdr,
|
||||
struct kexec_elf_info *elf_info)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = elf_read_ehdr(buf, len, ehdr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
elf_info->buffer = buf;
|
||||
elf_info->ehdr = ehdr;
|
||||
if (ehdr->e_phoff > 0 && ehdr->e_phnum > 0) {
|
||||
ret = elf_read_phdrs(buf, len, elf_info);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* kexec_free_elf_info - free memory allocated by elf_read_from_buffer
|
||||
*/
|
||||
void kexec_free_elf_info(struct kexec_elf_info *elf_info)
|
||||
{
|
||||
kfree(elf_info->proghdrs);
|
||||
memset(elf_info, 0, sizeof(*elf_info));
|
||||
}
|
||||
/**
|
||||
* kexec_build_elf_info - read ELF executable and check that we can use it
|
||||
*/
|
||||
int kexec_build_elf_info(const char *buf, size_t len, struct elfhdr *ehdr,
|
||||
struct kexec_elf_info *elf_info)
|
||||
{
|
||||
int i;
|
||||
int ret;
|
||||
|
||||
ret = elf_read_from_buffer(buf, len, ehdr, elf_info);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Big endian vmlinux has type ET_DYN. */
|
||||
if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) {
|
||||
pr_err("Not an ELF executable.\n");
|
||||
goto error;
|
||||
} else if (!elf_info->proghdrs) {
|
||||
pr_err("No ELF program header.\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
for (i = 0; i < ehdr->e_phnum; i++) {
|
||||
/*
|
||||
* Kexec does not support loading interpreters.
|
||||
* In addition this check keeps us from attempting
|
||||
* to kexec ordinay executables.
|
||||
*/
|
||||
if (elf_info->proghdrs[i].p_type == PT_INTERP) {
|
||||
pr_err("Requires an ELF interpreter.\n");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
error:
|
||||
kexec_free_elf_info(elf_info);
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
|
||||
int kexec_elf_probe(const char *buf, unsigned long len)
|
||||
{
|
||||
struct elfhdr ehdr;
|
||||
struct kexec_elf_info elf_info;
|
||||
int ret;
|
||||
|
||||
ret = kexec_build_elf_info(buf, len, &ehdr, &elf_info);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
kexec_free_elf_info(&elf_info);
|
||||
|
||||
return elf_check_arch(&ehdr) ? 0 : -ENOEXEC;
|
||||
}
|
||||
|
||||
/**
|
||||
* kexec_elf_load - load ELF executable image
|
||||
* @lowest_load_addr: On return, will be the address where the first PT_LOAD
|
||||
* section will be loaded in memory.
|
||||
*
|
||||
* Return:
|
||||
* 0 on success, negative value on failure.
|
||||
*/
|
||||
int kexec_elf_load(struct kimage *image, struct elfhdr *ehdr,
|
||||
struct kexec_elf_info *elf_info,
|
||||
struct kexec_buf *kbuf,
|
||||
unsigned long *lowest_load_addr)
|
||||
{
|
||||
unsigned long lowest_addr = UINT_MAX;
|
||||
int ret;
|
||||
size_t i;
|
||||
|
||||
/* Read in the PT_LOAD segments. */
|
||||
for (i = 0; i < ehdr->e_phnum; i++) {
|
||||
unsigned long load_addr;
|
||||
size_t size;
|
||||
const struct elf_phdr *phdr;
|
||||
|
||||
phdr = &elf_info->proghdrs[i];
|
||||
if (phdr->p_type != PT_LOAD)
|
||||
continue;
|
||||
|
||||
size = phdr->p_filesz;
|
||||
if (size > phdr->p_memsz)
|
||||
size = phdr->p_memsz;
|
||||
|
||||
kbuf->buffer = (void *) elf_info->buffer + phdr->p_offset;
|
||||
kbuf->bufsz = size;
|
||||
kbuf->memsz = phdr->p_memsz;
|
||||
kbuf->buf_align = phdr->p_align;
|
||||
kbuf->buf_min = phdr->p_paddr;
|
||||
kbuf->mem = KEXEC_BUF_MEM_UNKNOWN;
|
||||
ret = kexec_add_buffer(kbuf);
|
||||
if (ret)
|
||||
goto out;
|
||||
load_addr = kbuf->mem;
|
||||
|
||||
if (load_addr < lowest_addr)
|
||||
lowest_addr = load_addr;
|
||||
}
|
||||
|
||||
*lowest_load_addr = lowest_addr;
|
||||
ret = 0;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -1514,7 +1514,8 @@ static int check_kprobe_address_safe(struct kprobe *p,
|
|||
/* Ensure it is not in reserved area nor out of text */
|
||||
if (!kernel_text_address((unsigned long) p->addr) ||
|
||||
within_kprobe_blacklist((unsigned long) p->addr) ||
|
||||
jump_label_text_reserved(p->addr, p->addr)) {
|
||||
jump_label_text_reserved(p->addr, p->addr) ||
|
||||
find_bug((unsigned long)p->addr)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
|
@ -1906,7 +1907,7 @@ int register_kretprobe(struct kretprobe *rp)
|
|||
|
||||
/* Pre-allocate memory for max kretprobe instances */
|
||||
if (rp->maxactive <= 0) {
|
||||
#ifdef CONFIG_PREEMPT
|
||||
#ifdef CONFIG_PREEMPTION
|
||||
rp->maxactive = max_t(unsigned int, 10, 2*num_possible_cpus());
|
||||
#else
|
||||
rp->maxactive = num_possible_cpus();
|
||||
|
|
|
|||
|
|
@ -449,33 +449,101 @@ static void print_lockdep_off(const char *bug_msg)
|
|||
unsigned long nr_stack_trace_entries;
|
||||
|
||||
#ifdef CONFIG_PROVE_LOCKING
|
||||
/**
|
||||
* struct lock_trace - single stack backtrace
|
||||
* @hash_entry: Entry in a stack_trace_hash[] list.
|
||||
* @hash: jhash() of @entries.
|
||||
* @nr_entries: Number of entries in @entries.
|
||||
* @entries: Actual stack backtrace.
|
||||
*/
|
||||
struct lock_trace {
|
||||
struct hlist_node hash_entry;
|
||||
u32 hash;
|
||||
u32 nr_entries;
|
||||
unsigned long entries[0] __aligned(sizeof(unsigned long));
|
||||
};
|
||||
#define LOCK_TRACE_SIZE_IN_LONGS \
|
||||
(sizeof(struct lock_trace) / sizeof(unsigned long))
|
||||
/*
|
||||
* Stack-trace: tightly packed array of stack backtrace
|
||||
* addresses. Protected by the graph_lock.
|
||||
* Stack-trace: sequence of lock_trace structures. Protected by the graph_lock.
|
||||
*/
|
||||
static unsigned long stack_trace[MAX_STACK_TRACE_ENTRIES];
|
||||
static struct hlist_head stack_trace_hash[STACK_TRACE_HASH_SIZE];
|
||||
|
||||
static int save_trace(struct lock_trace *trace)
|
||||
static bool traces_identical(struct lock_trace *t1, struct lock_trace *t2)
|
||||
{
|
||||
unsigned long *entries = stack_trace + nr_stack_trace_entries;
|
||||
return t1->hash == t2->hash && t1->nr_entries == t2->nr_entries &&
|
||||
memcmp(t1->entries, t2->entries,
|
||||
t1->nr_entries * sizeof(t1->entries[0])) == 0;
|
||||
}
|
||||
|
||||
static struct lock_trace *save_trace(void)
|
||||
{
|
||||
struct lock_trace *trace, *t2;
|
||||
struct hlist_head *hash_head;
|
||||
u32 hash;
|
||||
unsigned int max_entries;
|
||||
|
||||
trace->offset = nr_stack_trace_entries;
|
||||
max_entries = MAX_STACK_TRACE_ENTRIES - nr_stack_trace_entries;
|
||||
trace->nr_entries = stack_trace_save(entries, max_entries, 3);
|
||||
nr_stack_trace_entries += trace->nr_entries;
|
||||
BUILD_BUG_ON_NOT_POWER_OF_2(STACK_TRACE_HASH_SIZE);
|
||||
BUILD_BUG_ON(LOCK_TRACE_SIZE_IN_LONGS >= MAX_STACK_TRACE_ENTRIES);
|
||||
|
||||
if (nr_stack_trace_entries >= MAX_STACK_TRACE_ENTRIES-1) {
|
||||
trace = (struct lock_trace *)(stack_trace + nr_stack_trace_entries);
|
||||
max_entries = MAX_STACK_TRACE_ENTRIES - nr_stack_trace_entries -
|
||||
LOCK_TRACE_SIZE_IN_LONGS;
|
||||
trace->nr_entries = stack_trace_save(trace->entries, max_entries, 3);
|
||||
|
||||
if (nr_stack_trace_entries >= MAX_STACK_TRACE_ENTRIES -
|
||||
LOCK_TRACE_SIZE_IN_LONGS - 1) {
|
||||
if (!debug_locks_off_graph_unlock())
|
||||
return 0;
|
||||
return NULL;
|
||||
|
||||
print_lockdep_off("BUG: MAX_STACK_TRACE_ENTRIES too low!");
|
||||
dump_stack();
|
||||
|
||||
return 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return 1;
|
||||
hash = jhash(trace->entries, trace->nr_entries *
|
||||
sizeof(trace->entries[0]), 0);
|
||||
trace->hash = hash;
|
||||
hash_head = stack_trace_hash + (hash & (STACK_TRACE_HASH_SIZE - 1));
|
||||
hlist_for_each_entry(t2, hash_head, hash_entry) {
|
||||
if (traces_identical(trace, t2))
|
||||
return t2;
|
||||
}
|
||||
nr_stack_trace_entries += LOCK_TRACE_SIZE_IN_LONGS + trace->nr_entries;
|
||||
hlist_add_head(&trace->hash_entry, hash_head);
|
||||
|
||||
return trace;
|
||||
}
|
||||
|
||||
/* Return the number of stack traces in the stack_trace[] array. */
|
||||
u64 lockdep_stack_trace_count(void)
|
||||
{
|
||||
struct lock_trace *trace;
|
||||
u64 c = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(stack_trace_hash); i++) {
|
||||
hlist_for_each_entry(trace, &stack_trace_hash[i], hash_entry) {
|
||||
c++;
|
||||
}
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
/* Return the number of stack hash chains that have at least one stack trace. */
|
||||
u64 lockdep_stack_hash_count(void)
|
||||
{
|
||||
u64 c = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(stack_trace_hash); i++)
|
||||
if (!hlist_empty(&stack_trace_hash[i]))
|
||||
c++;
|
||||
|
||||
return c;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -511,7 +579,7 @@ static const char *usage_str[] =
|
|||
};
|
||||
#endif
|
||||
|
||||
const char * __get_key_name(struct lockdep_subclass_key *key, char *str)
|
||||
const char *__get_key_name(const struct lockdep_subclass_key *key, char *str)
|
||||
{
|
||||
return kallsyms_lookup((unsigned long)key, NULL, NULL, NULL, str);
|
||||
}
|
||||
|
|
@ -620,7 +688,7 @@ static void print_lock(struct held_lock *hlock)
|
|||
return;
|
||||
}
|
||||
|
||||
printk(KERN_CONT "%p", hlock->instance);
|
||||
printk(KERN_CONT "%px", hlock->instance);
|
||||
print_lock_name(lock);
|
||||
printk(KERN_CONT ", at: %pS\n", (void *)hlock->acquire_ip);
|
||||
}
|
||||
|
|
@ -1235,7 +1303,7 @@ static struct lock_list *alloc_list_entry(void)
|
|||
static int add_lock_to_list(struct lock_class *this,
|
||||
struct lock_class *links_to, struct list_head *head,
|
||||
unsigned long ip, int distance,
|
||||
struct lock_trace *trace)
|
||||
const struct lock_trace *trace)
|
||||
{
|
||||
struct lock_list *entry;
|
||||
/*
|
||||
|
|
@ -1249,7 +1317,7 @@ static int add_lock_to_list(struct lock_class *this,
|
|||
entry->class = this;
|
||||
entry->links_to = links_to;
|
||||
entry->distance = distance;
|
||||
entry->trace = *trace;
|
||||
entry->trace = trace;
|
||||
/*
|
||||
* Both allocation and removal are done under the graph lock; but
|
||||
* iteration is under RCU-sched; see look_up_lock_class() and
|
||||
|
|
@ -1470,11 +1538,10 @@ static inline int __bfs_backwards(struct lock_list *src_entry,
|
|||
|
||||
}
|
||||
|
||||
static void print_lock_trace(struct lock_trace *trace, unsigned int spaces)
|
||||
static void print_lock_trace(const struct lock_trace *trace,
|
||||
unsigned int spaces)
|
||||
{
|
||||
unsigned long *entries = stack_trace + trace->offset;
|
||||
|
||||
stack_trace_print(entries, trace->nr_entries, spaces);
|
||||
stack_trace_print(trace->entries, trace->nr_entries, spaces);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1489,7 +1556,7 @@ print_circular_bug_entry(struct lock_list *target, int depth)
|
|||
printk("\n-> #%u", depth);
|
||||
print_lock_name(target->class);
|
||||
printk(KERN_CONT ":\n");
|
||||
print_lock_trace(&target->trace, 6);
|
||||
print_lock_trace(target->trace, 6);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -1592,7 +1659,8 @@ static noinline void print_circular_bug(struct lock_list *this,
|
|||
if (!debug_locks_off_graph_unlock() || debug_locks_silent)
|
||||
return;
|
||||
|
||||
if (!save_trace(&this->trace))
|
||||
this->trace = save_trace();
|
||||
if (!this->trace)
|
||||
return;
|
||||
|
||||
depth = get_lock_depth(target);
|
||||
|
|
@ -1715,7 +1783,7 @@ check_path(struct lock_class *target, struct lock_list *src_entry,
|
|||
*/
|
||||
static noinline int
|
||||
check_noncircular(struct held_lock *src, struct held_lock *target,
|
||||
struct lock_trace *trace)
|
||||
struct lock_trace **const trace)
|
||||
{
|
||||
int ret;
|
||||
struct lock_list *uninitialized_var(target_entry);
|
||||
|
|
@ -1729,13 +1797,13 @@ check_noncircular(struct held_lock *src, struct held_lock *target,
|
|||
ret = check_path(hlock_class(target), &src_entry, &target_entry);
|
||||
|
||||
if (unlikely(!ret)) {
|
||||
if (!trace->nr_entries) {
|
||||
if (!*trace) {
|
||||
/*
|
||||
* If save_trace fails here, the printing might
|
||||
* trigger a WARN but because of the !nr_entries it
|
||||
* should not do bad things.
|
||||
*/
|
||||
save_trace(trace);
|
||||
*trace = save_trace();
|
||||
}
|
||||
|
||||
print_circular_bug(&src_entry, target_entry, src, target);
|
||||
|
|
@ -1859,7 +1927,7 @@ static void print_lock_class_header(struct lock_class *class, int depth)
|
|||
|
||||
len += printk("%*s %s", depth, "", usage_str[bit]);
|
||||
len += printk(KERN_CONT " at:\n");
|
||||
print_lock_trace(class->usage_traces + bit, len);
|
||||
print_lock_trace(class->usage_traces[bit], len);
|
||||
}
|
||||
}
|
||||
printk("%*s }\n", depth, "");
|
||||
|
|
@ -1884,7 +1952,7 @@ print_shortest_lock_dependencies(struct lock_list *leaf,
|
|||
do {
|
||||
print_lock_class_header(entry->class, depth);
|
||||
printk("%*s ... acquired at:\n", depth, "");
|
||||
print_lock_trace(&entry->trace, 2);
|
||||
print_lock_trace(entry->trace, 2);
|
||||
printk("\n");
|
||||
|
||||
if (depth == 0 && (entry != root)) {
|
||||
|
|
@ -1995,14 +2063,14 @@ print_bad_irq_dependency(struct task_struct *curr,
|
|||
print_lock_name(backwards_entry->class);
|
||||
pr_warn("\n... which became %s-irq-safe at:\n", irqclass);
|
||||
|
||||
print_lock_trace(backwards_entry->class->usage_traces + bit1, 1);
|
||||
print_lock_trace(backwards_entry->class->usage_traces[bit1], 1);
|
||||
|
||||
pr_warn("\nto a %s-irq-unsafe lock:\n", irqclass);
|
||||
print_lock_name(forwards_entry->class);
|
||||
pr_warn("\n... which became %s-irq-unsafe at:\n", irqclass);
|
||||
pr_warn("...");
|
||||
|
||||
print_lock_trace(forwards_entry->class->usage_traces + bit2, 1);
|
||||
print_lock_trace(forwards_entry->class->usage_traces[bit2], 1);
|
||||
|
||||
pr_warn("\nother info that might help us debug this:\n\n");
|
||||
print_irq_lock_scenario(backwards_entry, forwards_entry,
|
||||
|
|
@ -2011,13 +2079,15 @@ print_bad_irq_dependency(struct task_struct *curr,
|
|||
lockdep_print_held_locks(curr);
|
||||
|
||||
pr_warn("\nthe dependencies between %s-irq-safe lock and the holding lock:\n", irqclass);
|
||||
if (!save_trace(&prev_root->trace))
|
||||
prev_root->trace = save_trace();
|
||||
if (!prev_root->trace)
|
||||
return;
|
||||
print_shortest_lock_dependencies(backwards_entry, prev_root);
|
||||
|
||||
pr_warn("\nthe dependencies between the lock to be acquired");
|
||||
pr_warn(" and %s-irq-unsafe lock:\n", irqclass);
|
||||
if (!save_trace(&next_root->trace))
|
||||
next_root->trace = save_trace();
|
||||
if (!next_root->trace)
|
||||
return;
|
||||
print_shortest_lock_dependencies(forwards_entry, next_root);
|
||||
|
||||
|
|
@ -2369,7 +2439,8 @@ check_deadlock(struct task_struct *curr, struct held_lock *next)
|
|||
*/
|
||||
static int
|
||||
check_prev_add(struct task_struct *curr, struct held_lock *prev,
|
||||
struct held_lock *next, int distance, struct lock_trace *trace)
|
||||
struct held_lock *next, int distance,
|
||||
struct lock_trace **const trace)
|
||||
{
|
||||
struct lock_list *entry;
|
||||
int ret;
|
||||
|
|
@ -2444,8 +2515,11 @@ check_prev_add(struct task_struct *curr, struct held_lock *prev,
|
|||
return ret;
|
||||
#endif
|
||||
|
||||
if (!trace->nr_entries && !save_trace(trace))
|
||||
return 0;
|
||||
if (!*trace) {
|
||||
*trace = save_trace();
|
||||
if (!*trace)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ok, all validations passed, add the new lock
|
||||
|
|
@ -2453,14 +2527,14 @@ check_prev_add(struct task_struct *curr, struct held_lock *prev,
|
|||
*/
|
||||
ret = add_lock_to_list(hlock_class(next), hlock_class(prev),
|
||||
&hlock_class(prev)->locks_after,
|
||||
next->acquire_ip, distance, trace);
|
||||
next->acquire_ip, distance, *trace);
|
||||
|
||||
if (!ret)
|
||||
return 0;
|
||||
|
||||
ret = add_lock_to_list(hlock_class(prev), hlock_class(next),
|
||||
&hlock_class(next)->locks_before,
|
||||
next->acquire_ip, distance, trace);
|
||||
next->acquire_ip, distance, *trace);
|
||||
if (!ret)
|
||||
return 0;
|
||||
|
||||
|
|
@ -2476,7 +2550,7 @@ check_prev_add(struct task_struct *curr, struct held_lock *prev,
|
|||
static int
|
||||
check_prevs_add(struct task_struct *curr, struct held_lock *next)
|
||||
{
|
||||
struct lock_trace trace = { .nr_entries = 0 };
|
||||
struct lock_trace *trace = NULL;
|
||||
int depth = curr->lockdep_depth;
|
||||
struct held_lock *hlock;
|
||||
|
||||
|
|
@ -3015,7 +3089,7 @@ print_usage_bug(struct task_struct *curr, struct held_lock *this,
|
|||
print_lock(this);
|
||||
|
||||
pr_warn("{%s} state was registered at:\n", usage_str[prev_bit]);
|
||||
print_lock_trace(hlock_class(this)->usage_traces + prev_bit, 1);
|
||||
print_lock_trace(hlock_class(this)->usage_traces[prev_bit], 1);
|
||||
|
||||
print_irqtrace_events(curr);
|
||||
pr_warn("\nother info that might help us debug this:\n");
|
||||
|
|
@ -3096,7 +3170,8 @@ print_irq_inversion_bug(struct task_struct *curr,
|
|||
lockdep_print_held_locks(curr);
|
||||
|
||||
pr_warn("\nthe shortest dependencies between 2nd lock and 1st lock:\n");
|
||||
if (!save_trace(&root->trace))
|
||||
root->trace = save_trace();
|
||||
if (!root->trace)
|
||||
return;
|
||||
print_shortest_lock_dependencies(other, root);
|
||||
|
||||
|
|
@ -3580,7 +3655,7 @@ static int mark_lock(struct task_struct *curr, struct held_lock *this,
|
|||
|
||||
hlock_class(this)->usage_mask |= new_mask;
|
||||
|
||||
if (!save_trace(hlock_class(this)->usage_traces + new_bit))
|
||||
if (!(hlock_class(this)->usage_traces[new_bit] = save_trace()))
|
||||
return 0;
|
||||
|
||||
switch (new_bit) {
|
||||
|
|
@ -5157,6 +5232,12 @@ void __init lockdep_init(void)
|
|||
) / 1024
|
||||
);
|
||||
|
||||
#if defined(CONFIG_TRACE_IRQFLAGS) && defined(CONFIG_PROVE_LOCKING)
|
||||
printk(" memory used for stack traces: %zu kB\n",
|
||||
(sizeof(stack_trace) + sizeof(stack_trace_hash)) / 1024
|
||||
);
|
||||
#endif
|
||||
|
||||
printk(" per task-struct memory footprint: %zu bytes\n",
|
||||
sizeof(((struct task_struct *)NULL)->held_locks));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,6 +92,7 @@ static const unsigned long LOCKF_USED_IN_IRQ_READ =
|
|||
#define MAX_LOCKDEP_ENTRIES 16384UL
|
||||
#define MAX_LOCKDEP_CHAINS_BITS 15
|
||||
#define MAX_STACK_TRACE_ENTRIES 262144UL
|
||||
#define STACK_TRACE_HASH_SIZE 8192
|
||||
#else
|
||||
#define MAX_LOCKDEP_ENTRIES 32768UL
|
||||
|
||||
|
|
@ -102,6 +103,7 @@ static const unsigned long LOCKF_USED_IN_IRQ_READ =
|
|||
* addresses. Protected by the hash_lock.
|
||||
*/
|
||||
#define MAX_STACK_TRACE_ENTRIES 524288UL
|
||||
#define STACK_TRACE_HASH_SIZE 16384
|
||||
#endif
|
||||
|
||||
#define MAX_LOCKDEP_CHAINS (1UL << MAX_LOCKDEP_CHAINS_BITS)
|
||||
|
|
@ -116,7 +118,8 @@ extern struct lock_chain lock_chains[];
|
|||
extern void get_usage_chars(struct lock_class *class,
|
||||
char usage[LOCK_USAGE_CHARS]);
|
||||
|
||||
extern const char * __get_key_name(struct lockdep_subclass_key *key, char *str);
|
||||
extern const char *__get_key_name(const struct lockdep_subclass_key *key,
|
||||
char *str);
|
||||
|
||||
struct lock_class *lock_chain_get_class(struct lock_chain *chain, int i);
|
||||
|
||||
|
|
@ -137,6 +140,10 @@ extern unsigned int max_bfs_queue_depth;
|
|||
#ifdef CONFIG_PROVE_LOCKING
|
||||
extern unsigned long lockdep_count_forward_deps(struct lock_class *);
|
||||
extern unsigned long lockdep_count_backward_deps(struct lock_class *);
|
||||
#ifdef CONFIG_TRACE_IRQFLAGS
|
||||
u64 lockdep_stack_trace_count(void);
|
||||
u64 lockdep_stack_hash_count(void);
|
||||
#endif
|
||||
#else
|
||||
static inline unsigned long
|
||||
lockdep_count_forward_deps(struct lock_class *class)
|
||||
|
|
|
|||
|
|
@ -285,6 +285,12 @@ static int lockdep_stats_show(struct seq_file *m, void *v)
|
|||
nr_process_chains);
|
||||
seq_printf(m, " stack-trace entries: %11lu [max: %lu]\n",
|
||||
nr_stack_trace_entries, MAX_STACK_TRACE_ENTRIES);
|
||||
#if defined(CONFIG_TRACE_IRQFLAGS) && defined(CONFIG_PROVE_LOCKING)
|
||||
seq_printf(m, " number of stack traces: %llu\n",
|
||||
lockdep_stack_trace_count());
|
||||
seq_printf(m, " number of stack hash chains: %llu\n",
|
||||
lockdep_stack_hash_count());
|
||||
#endif
|
||||
seq_printf(m, " combined max dependencies: %11u\n",
|
||||
(nr_hardirq_chains + 1) *
|
||||
(nr_softirq_chains + 1) *
|
||||
|
|
@ -399,7 +405,7 @@ static void seq_lock_time(struct seq_file *m, struct lock_time *lt)
|
|||
|
||||
static void seq_stats(struct seq_file *m, struct lock_stat_data *data)
|
||||
{
|
||||
struct lockdep_subclass_key *ckey;
|
||||
const struct lockdep_subclass_key *ckey;
|
||||
struct lock_class_stats *stats;
|
||||
struct lock_class *class;
|
||||
const char *cname;
|
||||
|
|
|
|||
|
|
@ -65,11 +65,37 @@ EXPORT_SYMBOL(__mutex_init);
|
|||
|
||||
#define MUTEX_FLAGS 0x07
|
||||
|
||||
/*
|
||||
* Internal helper function; C doesn't allow us to hide it :/
|
||||
*
|
||||
* DO NOT USE (outside of mutex code).
|
||||
*/
|
||||
static inline struct task_struct *__mutex_owner(struct mutex *lock)
|
||||
{
|
||||
return (struct task_struct *)(atomic_long_read(&lock->owner) & ~MUTEX_FLAGS);
|
||||
}
|
||||
|
||||
static inline struct task_struct *__owner_task(unsigned long owner)
|
||||
{
|
||||
return (struct task_struct *)(owner & ~MUTEX_FLAGS);
|
||||
}
|
||||
|
||||
bool mutex_is_locked(struct mutex *lock)
|
||||
{
|
||||
return __mutex_owner(lock) != NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(mutex_is_locked);
|
||||
|
||||
__must_check enum mutex_trylock_recursive_enum
|
||||
mutex_trylock_recursive(struct mutex *lock)
|
||||
{
|
||||
if (unlikely(__mutex_owner(lock) == current))
|
||||
return MUTEX_TRYLOCK_RECURSIVE;
|
||||
|
||||
return mutex_trylock(lock);
|
||||
}
|
||||
EXPORT_SYMBOL(mutex_trylock_recursive);
|
||||
|
||||
static inline unsigned long __owner_flags(unsigned long owner)
|
||||
{
|
||||
return owner & MUTEX_FLAGS;
|
||||
|
|
|
|||
|
|
@ -628,8 +628,7 @@ static int rt_mutex_adjust_prio_chain(struct task_struct *task,
|
|||
}
|
||||
|
||||
/* [10] Grab the next task, i.e. owner of @lock */
|
||||
task = rt_mutex_owner(lock);
|
||||
get_task_struct(task);
|
||||
task = get_task_struct(rt_mutex_owner(lock));
|
||||
raw_spin_lock(&task->pi_lock);
|
||||
|
||||
/*
|
||||
|
|
@ -709,8 +708,7 @@ static int rt_mutex_adjust_prio_chain(struct task_struct *task,
|
|||
}
|
||||
|
||||
/* [10] Grab the next task, i.e. the owner of @lock */
|
||||
task = rt_mutex_owner(lock);
|
||||
get_task_struct(task);
|
||||
task = get_task_struct(rt_mutex_owner(lock));
|
||||
raw_spin_lock(&task->pi_lock);
|
||||
|
||||
/* [11] requeue the pi waiters if necessary */
|
||||
|
|
|
|||
|
|
@ -105,8 +105,9 @@
|
|||
#ifdef CONFIG_DEBUG_RWSEMS
|
||||
# define DEBUG_RWSEMS_WARN_ON(c, sem) do { \
|
||||
if (!debug_locks_silent && \
|
||||
WARN_ONCE(c, "DEBUG_RWSEMS_WARN_ON(%s): count = 0x%lx, owner = 0x%lx, curr 0x%lx, list %sempty\n",\
|
||||
WARN_ONCE(c, "DEBUG_RWSEMS_WARN_ON(%s): count = 0x%lx, magic = 0x%lx, owner = 0x%lx, curr 0x%lx, list %sempty\n",\
|
||||
#c, atomic_long_read(&(sem)->count), \
|
||||
(unsigned long) sem->magic, \
|
||||
atomic_long_read(&(sem)->owner), (long)current, \
|
||||
list_empty(&(sem)->wait_list) ? "" : "not ")) \
|
||||
debug_locks_off(); \
|
||||
|
|
@ -329,6 +330,9 @@ void __init_rwsem(struct rw_semaphore *sem, const char *name,
|
|||
*/
|
||||
debug_check_no_locks_freed((void *)sem, sizeof(*sem));
|
||||
lockdep_init_map(&sem->dep_map, name, key, 0);
|
||||
#endif
|
||||
#ifdef CONFIG_DEBUG_RWSEMS
|
||||
sem->magic = sem;
|
||||
#endif
|
||||
atomic_long_set(&sem->count, RWSEM_UNLOCKED_VALUE);
|
||||
raw_spin_lock_init(&sem->wait_lock);
|
||||
|
|
@ -724,11 +728,12 @@ rwsem_spin_on_owner(struct rw_semaphore *sem, unsigned long nonspinnable)
|
|||
|
||||
rcu_read_lock();
|
||||
for (;;) {
|
||||
if (atomic_long_read(&sem->count) & RWSEM_FLAG_HANDOFF) {
|
||||
state = OWNER_NONSPINNABLE;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* When a waiting writer set the handoff flag, it may spin
|
||||
* on the owner as well. Once that writer acquires the lock,
|
||||
* we can spin on it. So we don't need to quit even when the
|
||||
* handoff bit is set.
|
||||
*/
|
||||
new = rwsem_owner_flags(sem, &new_flags);
|
||||
if ((new != owner) || (new_flags != flags)) {
|
||||
state = rwsem_owner_state(new, new_flags, nonspinnable);
|
||||
|
|
@ -974,6 +979,13 @@ static inline bool rwsem_reader_phase_trylock(struct rw_semaphore *sem,
|
|||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline int
|
||||
rwsem_spin_on_owner(struct rw_semaphore *sem, unsigned long nonspinnable)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#define OWNER_NULL 1
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
|
@ -1206,6 +1218,18 @@ wait:
|
|||
|
||||
raw_spin_unlock_irq(&sem->wait_lock);
|
||||
|
||||
/*
|
||||
* After setting the handoff bit and failing to acquire
|
||||
* the lock, attempt to spin on owner to accelerate lock
|
||||
* transfer. If the previous owner is a on-cpu writer and it
|
||||
* has just released the lock, OWNER_NULL will be returned.
|
||||
* In this case, we attempt to acquire the lock again
|
||||
* without sleeping.
|
||||
*/
|
||||
if ((wstate == WRITER_HANDOFF) &&
|
||||
(rwsem_spin_on_owner(sem, 0) == OWNER_NULL))
|
||||
goto trylock_again;
|
||||
|
||||
/* Block until there are no active lockers. */
|
||||
for (;;) {
|
||||
if (signal_pending_state(state, current))
|
||||
|
|
@ -1240,7 +1264,7 @@ wait:
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
trylock_again:
|
||||
raw_spin_lock_irq(&sem->wait_lock);
|
||||
}
|
||||
__set_current_state(TASK_RUNNING);
|
||||
|
|
@ -1338,11 +1362,14 @@ static inline int __down_read_killable(struct rw_semaphore *sem)
|
|||
|
||||
static inline int __down_read_trylock(struct rw_semaphore *sem)
|
||||
{
|
||||
long tmp;
|
||||
|
||||
DEBUG_RWSEMS_WARN_ON(sem->magic != sem, sem);
|
||||
|
||||
/*
|
||||
* Optimize for the case when the rwsem is not locked at all.
|
||||
*/
|
||||
long tmp = RWSEM_UNLOCKED_VALUE;
|
||||
|
||||
tmp = RWSEM_UNLOCKED_VALUE;
|
||||
do {
|
||||
if (atomic_long_try_cmpxchg_acquire(&sem->count, &tmp,
|
||||
tmp + RWSEM_READER_BIAS)) {
|
||||
|
|
@ -1383,8 +1410,11 @@ static inline int __down_write_killable(struct rw_semaphore *sem)
|
|||
|
||||
static inline int __down_write_trylock(struct rw_semaphore *sem)
|
||||
{
|
||||
long tmp = RWSEM_UNLOCKED_VALUE;
|
||||
long tmp;
|
||||
|
||||
DEBUG_RWSEMS_WARN_ON(sem->magic != sem, sem);
|
||||
|
||||
tmp = RWSEM_UNLOCKED_VALUE;
|
||||
if (atomic_long_try_cmpxchg_acquire(&sem->count, &tmp,
|
||||
RWSEM_WRITER_LOCKED)) {
|
||||
rwsem_set_owner(sem);
|
||||
|
|
@ -1400,7 +1430,9 @@ inline void __up_read(struct rw_semaphore *sem)
|
|||
{
|
||||
long tmp;
|
||||
|
||||
DEBUG_RWSEMS_WARN_ON(sem->magic != sem, sem);
|
||||
DEBUG_RWSEMS_WARN_ON(!is_rwsem_reader_owned(sem), sem);
|
||||
|
||||
rwsem_clear_reader_owned(sem);
|
||||
tmp = atomic_long_add_return_release(-RWSEM_READER_BIAS, &sem->count);
|
||||
DEBUG_RWSEMS_WARN_ON(tmp < 0, sem);
|
||||
|
|
@ -1418,12 +1450,14 @@ static inline void __up_write(struct rw_semaphore *sem)
|
|||
{
|
||||
long tmp;
|
||||
|
||||
DEBUG_RWSEMS_WARN_ON(sem->magic != sem, sem);
|
||||
/*
|
||||
* sem->owner may differ from current if the ownership is transferred
|
||||
* to an anonymous writer by setting the RWSEM_NONSPINNABLE bits.
|
||||
*/
|
||||
DEBUG_RWSEMS_WARN_ON((rwsem_owner(sem) != current) &&
|
||||
!rwsem_test_oflags(sem, RWSEM_NONSPINNABLE), sem);
|
||||
|
||||
rwsem_clear_owner(sem);
|
||||
tmp = atomic_long_fetch_add_release(-RWSEM_WRITER_LOCKED, &sem->count);
|
||||
if (unlikely(tmp & RWSEM_FLAG_WAITERS))
|
||||
|
|
|
|||
311
kernel/padata.c
311
kernel/padata.c
|
|
@ -46,18 +46,13 @@ static int padata_index_to_cpu(struct parallel_data *pd, int cpu_index)
|
|||
return target_cpu;
|
||||
}
|
||||
|
||||
static int padata_cpu_hash(struct parallel_data *pd)
|
||||
static int padata_cpu_hash(struct parallel_data *pd, unsigned int seq_nr)
|
||||
{
|
||||
unsigned int seq_nr;
|
||||
int cpu_index;
|
||||
|
||||
/*
|
||||
* Hash the sequence numbers to the cpus by taking
|
||||
* seq_nr mod. number of cpus in use.
|
||||
*/
|
||||
|
||||
seq_nr = atomic_inc_return(&pd->seq_nr);
|
||||
cpu_index = seq_nr % cpumask_weight(pd->cpumask.pcpu);
|
||||
int cpu_index = seq_nr % cpumask_weight(pd->cpumask.pcpu);
|
||||
|
||||
return padata_index_to_cpu(pd, cpu_index);
|
||||
}
|
||||
|
|
@ -94,17 +89,19 @@ static void padata_parallel_worker(struct work_struct *parallel_work)
|
|||
*
|
||||
* @pinst: padata instance
|
||||
* @padata: object to be parallelized
|
||||
* @cb_cpu: cpu the serialization callback function will run on,
|
||||
* must be in the serial cpumask of padata(i.e. cpumask.cbcpu).
|
||||
* @cb_cpu: pointer to the CPU that the serialization callback function should
|
||||
* run on. If it's not in the serial cpumask of @pinst
|
||||
* (i.e. cpumask.cbcpu), this function selects a fallback CPU and if
|
||||
* none found, returns -EINVAL.
|
||||
*
|
||||
* The parallelization callback function will run with BHs off.
|
||||
* Note: Every object which is parallelized by padata_do_parallel
|
||||
* must be seen by padata_do_serial.
|
||||
*/
|
||||
int padata_do_parallel(struct padata_instance *pinst,
|
||||
struct padata_priv *padata, int cb_cpu)
|
||||
struct padata_priv *padata, int *cb_cpu)
|
||||
{
|
||||
int target_cpu, err;
|
||||
int i, cpu, cpu_index, target_cpu, err;
|
||||
struct padata_parallel_queue *queue;
|
||||
struct parallel_data *pd;
|
||||
|
||||
|
|
@ -116,8 +113,19 @@ int padata_do_parallel(struct padata_instance *pinst,
|
|||
if (!(pinst->flags & PADATA_INIT) || pinst->flags & PADATA_INVALID)
|
||||
goto out;
|
||||
|
||||
if (!cpumask_test_cpu(cb_cpu, pd->cpumask.cbcpu))
|
||||
goto out;
|
||||
if (!cpumask_test_cpu(*cb_cpu, pd->cpumask.cbcpu)) {
|
||||
if (!cpumask_weight(pd->cpumask.cbcpu))
|
||||
goto out;
|
||||
|
||||
/* Select an alternate fallback CPU and notify the caller. */
|
||||
cpu_index = *cb_cpu % cpumask_weight(pd->cpumask.cbcpu);
|
||||
|
||||
cpu = cpumask_first(pd->cpumask.cbcpu);
|
||||
for (i = 0; i < cpu_index; i++)
|
||||
cpu = cpumask_next(cpu, pd->cpumask.cbcpu);
|
||||
|
||||
*cb_cpu = cpu;
|
||||
}
|
||||
|
||||
err = -EBUSY;
|
||||
if ((pinst->flags & PADATA_RESET))
|
||||
|
|
@ -129,9 +137,10 @@ int padata_do_parallel(struct padata_instance *pinst,
|
|||
err = 0;
|
||||
atomic_inc(&pd->refcnt);
|
||||
padata->pd = pd;
|
||||
padata->cb_cpu = cb_cpu;
|
||||
padata->cb_cpu = *cb_cpu;
|
||||
|
||||
target_cpu = padata_cpu_hash(pd);
|
||||
padata->seq_nr = atomic_inc_return(&pd->seq_nr);
|
||||
target_cpu = padata_cpu_hash(pd, padata->seq_nr);
|
||||
padata->cpu = target_cpu;
|
||||
queue = per_cpu_ptr(pd->pqueue, target_cpu);
|
||||
|
||||
|
|
@ -139,7 +148,7 @@ int padata_do_parallel(struct padata_instance *pinst,
|
|||
list_add_tail(&padata->list, &queue->parallel.list);
|
||||
spin_unlock(&queue->parallel.lock);
|
||||
|
||||
queue_work_on(target_cpu, pinst->wq, &queue->work);
|
||||
queue_work(pinst->parallel_wq, &queue->work);
|
||||
|
||||
out:
|
||||
rcu_read_unlock_bh();
|
||||
|
|
@ -149,63 +158,53 @@ out:
|
|||
EXPORT_SYMBOL(padata_do_parallel);
|
||||
|
||||
/*
|
||||
* padata_get_next - Get the next object that needs serialization.
|
||||
* padata_find_next - Find the next object that needs serialization.
|
||||
*
|
||||
* Return values are:
|
||||
*
|
||||
* A pointer to the control struct of the next object that needs
|
||||
* serialization, if present in one of the percpu reorder queues.
|
||||
*
|
||||
* -EINPROGRESS, if the next object that needs serialization will
|
||||
* NULL, if the next object that needs serialization will
|
||||
* be parallel processed by another cpu and is not yet present in
|
||||
* the cpu's reorder queue.
|
||||
*
|
||||
* -ENODATA, if this cpu has to do the parallel processing for
|
||||
* the next object.
|
||||
*/
|
||||
static struct padata_priv *padata_get_next(struct parallel_data *pd)
|
||||
static struct padata_priv *padata_find_next(struct parallel_data *pd,
|
||||
bool remove_object)
|
||||
{
|
||||
int cpu, num_cpus;
|
||||
unsigned int next_nr, next_index;
|
||||
struct padata_parallel_queue *next_queue;
|
||||
struct padata_priv *padata;
|
||||
struct padata_list *reorder;
|
||||
int cpu = pd->cpu;
|
||||
|
||||
num_cpus = cpumask_weight(pd->cpumask.pcpu);
|
||||
|
||||
/*
|
||||
* Calculate the percpu reorder queue and the sequence
|
||||
* number of the next object.
|
||||
*/
|
||||
next_nr = pd->processed;
|
||||
next_index = next_nr % num_cpus;
|
||||
cpu = padata_index_to_cpu(pd, next_index);
|
||||
next_queue = per_cpu_ptr(pd->pqueue, cpu);
|
||||
|
||||
reorder = &next_queue->reorder;
|
||||
|
||||
spin_lock(&reorder->lock);
|
||||
if (!list_empty(&reorder->list)) {
|
||||
padata = list_entry(reorder->list.next,
|
||||
struct padata_priv, list);
|
||||
if (list_empty(&reorder->list)) {
|
||||
spin_unlock(&reorder->lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
padata = list_entry(reorder->list.next, struct padata_priv, list);
|
||||
|
||||
/*
|
||||
* Checks the rare case where two or more parallel jobs have hashed to
|
||||
* the same CPU and one of the later ones finishes first.
|
||||
*/
|
||||
if (padata->seq_nr != pd->processed) {
|
||||
spin_unlock(&reorder->lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (remove_object) {
|
||||
list_del_init(&padata->list);
|
||||
atomic_dec(&pd->reorder_objects);
|
||||
|
||||
pd->processed++;
|
||||
|
||||
spin_unlock(&reorder->lock);
|
||||
goto out;
|
||||
++pd->processed;
|
||||
pd->cpu = cpumask_next_wrap(cpu, pd->cpumask.pcpu, -1, false);
|
||||
}
|
||||
|
||||
spin_unlock(&reorder->lock);
|
||||
|
||||
if (__this_cpu_read(pd->pqueue->cpu_index) == next_queue->cpu_index) {
|
||||
padata = ERR_PTR(-ENODATA);
|
||||
goto out;
|
||||
}
|
||||
|
||||
padata = ERR_PTR(-EINPROGRESS);
|
||||
out:
|
||||
return padata;
|
||||
}
|
||||
|
||||
|
|
@ -215,6 +214,7 @@ static void padata_reorder(struct parallel_data *pd)
|
|||
struct padata_priv *padata;
|
||||
struct padata_serial_queue *squeue;
|
||||
struct padata_instance *pinst = pd->pinst;
|
||||
struct padata_parallel_queue *next_queue;
|
||||
|
||||
/*
|
||||
* We need to ensure that only one cpu can work on dequeueing of
|
||||
|
|
@ -230,27 +230,16 @@ static void padata_reorder(struct parallel_data *pd)
|
|||
return;
|
||||
|
||||
while (1) {
|
||||
padata = padata_get_next(pd);
|
||||
padata = padata_find_next(pd, true);
|
||||
|
||||
/*
|
||||
* If the next object that needs serialization is parallel
|
||||
* processed by another cpu and is still on it's way to the
|
||||
* cpu's reorder queue, nothing to do for now.
|
||||
*/
|
||||
if (PTR_ERR(padata) == -EINPROGRESS)
|
||||
if (!padata)
|
||||
break;
|
||||
|
||||
/*
|
||||
* This cpu has to do the parallel processing of the next
|
||||
* object. It's waiting in the cpu's parallelization queue,
|
||||
* so exit immediately.
|
||||
*/
|
||||
if (PTR_ERR(padata) == -ENODATA) {
|
||||
del_timer(&pd->timer);
|
||||
spin_unlock_bh(&pd->lock);
|
||||
return;
|
||||
}
|
||||
|
||||
cb_cpu = padata->cb_cpu;
|
||||
squeue = per_cpu_ptr(pd->squeue, cb_cpu);
|
||||
|
||||
|
|
@ -258,77 +247,37 @@ static void padata_reorder(struct parallel_data *pd)
|
|||
list_add_tail(&padata->list, &squeue->serial.list);
|
||||
spin_unlock(&squeue->serial.lock);
|
||||
|
||||
queue_work_on(cb_cpu, pinst->wq, &squeue->work);
|
||||
queue_work_on(cb_cpu, pinst->serial_wq, &squeue->work);
|
||||
}
|
||||
|
||||
spin_unlock_bh(&pd->lock);
|
||||
|
||||
/*
|
||||
* The next object that needs serialization might have arrived to
|
||||
* the reorder queues in the meantime, we will be called again
|
||||
* from the timer function if no one else cares for it.
|
||||
* the reorder queues in the meantime.
|
||||
*
|
||||
* Ensure reorder_objects is read after pd->lock is dropped so we see
|
||||
* an increment from another task in padata_do_serial. Pairs with
|
||||
* Ensure reorder queue is read after pd->lock is dropped so we see
|
||||
* new objects from another task in padata_do_serial. Pairs with
|
||||
* smp_mb__after_atomic in padata_do_serial.
|
||||
*/
|
||||
smp_mb();
|
||||
if (atomic_read(&pd->reorder_objects)
|
||||
&& !(pinst->flags & PADATA_RESET))
|
||||
mod_timer(&pd->timer, jiffies + HZ);
|
||||
else
|
||||
del_timer(&pd->timer);
|
||||
|
||||
return;
|
||||
next_queue = per_cpu_ptr(pd->pqueue, pd->cpu);
|
||||
if (!list_empty(&next_queue->reorder.list) &&
|
||||
padata_find_next(pd, false))
|
||||
queue_work(pinst->serial_wq, &pd->reorder_work);
|
||||
}
|
||||
|
||||
static void invoke_padata_reorder(struct work_struct *work)
|
||||
{
|
||||
struct padata_parallel_queue *pqueue;
|
||||
struct parallel_data *pd;
|
||||
|
||||
local_bh_disable();
|
||||
pqueue = container_of(work, struct padata_parallel_queue, reorder_work);
|
||||
pd = pqueue->pd;
|
||||
pd = container_of(work, struct parallel_data, reorder_work);
|
||||
padata_reorder(pd);
|
||||
local_bh_enable();
|
||||
}
|
||||
|
||||
static void padata_reorder_timer(struct timer_list *t)
|
||||
{
|
||||
struct parallel_data *pd = from_timer(pd, t, timer);
|
||||
unsigned int weight;
|
||||
int target_cpu, cpu;
|
||||
|
||||
cpu = get_cpu();
|
||||
|
||||
/* We don't lock pd here to not interfere with parallel processing
|
||||
* padata_reorder() calls on other CPUs. We just need any CPU out of
|
||||
* the cpumask.pcpu set. It would be nice if it's the right one but
|
||||
* it doesn't matter if we're off to the next one by using an outdated
|
||||
* pd->processed value.
|
||||
*/
|
||||
weight = cpumask_weight(pd->cpumask.pcpu);
|
||||
target_cpu = padata_index_to_cpu(pd, pd->processed % weight);
|
||||
|
||||
/* ensure to call the reorder callback on the correct CPU */
|
||||
if (cpu != target_cpu) {
|
||||
struct padata_parallel_queue *pqueue;
|
||||
struct padata_instance *pinst;
|
||||
|
||||
/* The timer function is serialized wrt itself -- no locking
|
||||
* needed.
|
||||
*/
|
||||
pinst = pd->pinst;
|
||||
pqueue = per_cpu_ptr(pd->pqueue, target_cpu);
|
||||
queue_work_on(target_cpu, pinst->wq, &pqueue->reorder_work);
|
||||
} else {
|
||||
padata_reorder(pd);
|
||||
}
|
||||
|
||||
put_cpu();
|
||||
}
|
||||
|
||||
static void padata_serial_worker(struct work_struct *serial_work)
|
||||
{
|
||||
struct padata_serial_queue *squeue;
|
||||
|
|
@ -367,47 +316,28 @@ static void padata_serial_worker(struct work_struct *serial_work)
|
|||
*/
|
||||
void padata_do_serial(struct padata_priv *padata)
|
||||
{
|
||||
int cpu;
|
||||
struct padata_parallel_queue *pqueue;
|
||||
struct parallel_data *pd;
|
||||
int reorder_via_wq = 0;
|
||||
|
||||
pd = padata->pd;
|
||||
|
||||
cpu = get_cpu();
|
||||
|
||||
/* We need to run on the same CPU padata_do_parallel(.., padata, ..)
|
||||
* was called on -- or, at least, enqueue the padata object into the
|
||||
* correct per-cpu queue.
|
||||
*/
|
||||
if (cpu != padata->cpu) {
|
||||
reorder_via_wq = 1;
|
||||
cpu = padata->cpu;
|
||||
}
|
||||
|
||||
pqueue = per_cpu_ptr(pd->pqueue, cpu);
|
||||
struct parallel_data *pd = padata->pd;
|
||||
struct padata_parallel_queue *pqueue = per_cpu_ptr(pd->pqueue,
|
||||
padata->cpu);
|
||||
struct padata_priv *cur;
|
||||
|
||||
spin_lock(&pqueue->reorder.lock);
|
||||
/* Sort in ascending order of sequence number. */
|
||||
list_for_each_entry_reverse(cur, &pqueue->reorder.list, list)
|
||||
if (cur->seq_nr < padata->seq_nr)
|
||||
break;
|
||||
list_add(&padata->list, &cur->list);
|
||||
atomic_inc(&pd->reorder_objects);
|
||||
list_add_tail(&padata->list, &pqueue->reorder.list);
|
||||
spin_unlock(&pqueue->reorder.lock);
|
||||
|
||||
/*
|
||||
* Ensure the atomic_inc of reorder_objects above is ordered correctly
|
||||
* Ensure the addition to the reorder list is ordered correctly
|
||||
* with the trylock of pd->lock in padata_reorder. Pairs with smp_mb
|
||||
* in padata_reorder.
|
||||
*/
|
||||
smp_mb__after_atomic();
|
||||
|
||||
put_cpu();
|
||||
|
||||
/* If we're running on the wrong CPU, call padata_reorder() via a
|
||||
* kernel worker.
|
||||
*/
|
||||
if (reorder_via_wq)
|
||||
queue_work_on(cpu, pd->pinst->wq, &pqueue->reorder_work);
|
||||
else
|
||||
padata_reorder(pd);
|
||||
padata_reorder(pd);
|
||||
}
|
||||
EXPORT_SYMBOL(padata_do_serial);
|
||||
|
||||
|
|
@ -415,17 +345,36 @@ static int padata_setup_cpumasks(struct parallel_data *pd,
|
|||
const struct cpumask *pcpumask,
|
||||
const struct cpumask *cbcpumask)
|
||||
{
|
||||
struct workqueue_attrs *attrs;
|
||||
int err = -ENOMEM;
|
||||
|
||||
if (!alloc_cpumask_var(&pd->cpumask.pcpu, GFP_KERNEL))
|
||||
return -ENOMEM;
|
||||
|
||||
goto out;
|
||||
cpumask_and(pd->cpumask.pcpu, pcpumask, cpu_online_mask);
|
||||
if (!alloc_cpumask_var(&pd->cpumask.cbcpu, GFP_KERNEL)) {
|
||||
free_cpumask_var(pd->cpumask.pcpu);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (!alloc_cpumask_var(&pd->cpumask.cbcpu, GFP_KERNEL))
|
||||
goto free_pcpu_mask;
|
||||
cpumask_and(pd->cpumask.cbcpu, cbcpumask, cpu_online_mask);
|
||||
|
||||
attrs = alloc_workqueue_attrs();
|
||||
if (!attrs)
|
||||
goto free_cbcpu_mask;
|
||||
|
||||
/* Restrict parallel_wq workers to pd->cpumask.pcpu. */
|
||||
cpumask_copy(attrs->cpumask, pd->cpumask.pcpu);
|
||||
err = apply_workqueue_attrs(pd->pinst->parallel_wq, attrs);
|
||||
free_workqueue_attrs(attrs);
|
||||
if (err < 0)
|
||||
goto free_cbcpu_mask;
|
||||
|
||||
return 0;
|
||||
|
||||
free_cbcpu_mask:
|
||||
free_cpumask_var(pd->cpumask.cbcpu);
|
||||
free_pcpu_mask:
|
||||
free_cpumask_var(pd->cpumask.pcpu);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __padata_list_init(struct padata_list *pd_list)
|
||||
|
|
@ -451,26 +400,15 @@ static void padata_init_squeues(struct parallel_data *pd)
|
|||
/* Initialize all percpu queues used by parallel workers */
|
||||
static void padata_init_pqueues(struct parallel_data *pd)
|
||||
{
|
||||
int cpu_index, cpu;
|
||||
int cpu;
|
||||
struct padata_parallel_queue *pqueue;
|
||||
|
||||
cpu_index = 0;
|
||||
for_each_possible_cpu(cpu) {
|
||||
for_each_cpu(cpu, pd->cpumask.pcpu) {
|
||||
pqueue = per_cpu_ptr(pd->pqueue, cpu);
|
||||
|
||||
if (!cpumask_test_cpu(cpu, pd->cpumask.pcpu)) {
|
||||
pqueue->cpu_index = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
pqueue->pd = pd;
|
||||
pqueue->cpu_index = cpu_index;
|
||||
cpu_index++;
|
||||
|
||||
__padata_list_init(&pqueue->reorder);
|
||||
__padata_list_init(&pqueue->parallel);
|
||||
INIT_WORK(&pqueue->work, padata_parallel_worker);
|
||||
INIT_WORK(&pqueue->reorder_work, invoke_padata_reorder);
|
||||
atomic_set(&pqueue->num_obj, 0);
|
||||
}
|
||||
}
|
||||
|
|
@ -493,17 +431,19 @@ static struct parallel_data *padata_alloc_pd(struct padata_instance *pinst,
|
|||
pd->squeue = alloc_percpu(struct padata_serial_queue);
|
||||
if (!pd->squeue)
|
||||
goto err_free_pqueue;
|
||||
|
||||
pd->pinst = pinst;
|
||||
if (padata_setup_cpumasks(pd, pcpumask, cbcpumask) < 0)
|
||||
goto err_free_squeue;
|
||||
|
||||
padata_init_pqueues(pd);
|
||||
padata_init_squeues(pd);
|
||||
timer_setup(&pd->timer, padata_reorder_timer, 0);
|
||||
atomic_set(&pd->seq_nr, -1);
|
||||
atomic_set(&pd->reorder_objects, 0);
|
||||
atomic_set(&pd->refcnt, 0);
|
||||
pd->pinst = pinst;
|
||||
spin_lock_init(&pd->lock);
|
||||
pd->cpu = cpumask_first(pd->cpumask.pcpu);
|
||||
INIT_WORK(&pd->reorder_work, invoke_padata_reorder);
|
||||
|
||||
return pd;
|
||||
|
||||
|
|
@ -538,8 +478,6 @@ static void padata_flush_queues(struct parallel_data *pd)
|
|||
flush_work(&pqueue->work);
|
||||
}
|
||||
|
||||
del_timer_sync(&pd->timer);
|
||||
|
||||
if (atomic_read(&pd->reorder_objects))
|
||||
padata_reorder(pd);
|
||||
|
||||
|
|
@ -883,6 +821,8 @@ static void __padata_free(struct padata_instance *pinst)
|
|||
padata_free_pd(pinst->pd);
|
||||
free_cpumask_var(pinst->cpumask.pcpu);
|
||||
free_cpumask_var(pinst->cpumask.cbcpu);
|
||||
destroy_workqueue(pinst->serial_wq);
|
||||
destroy_workqueue(pinst->parallel_wq);
|
||||
kfree(pinst);
|
||||
}
|
||||
|
||||
|
|
@ -1016,13 +956,11 @@ static struct kobj_type padata_attr_type = {
|
|||
* padata_alloc - allocate and initialize a padata instance and specify
|
||||
* cpumasks for serial and parallel workers.
|
||||
*
|
||||
* @wq: workqueue to use for the allocated padata instance
|
||||
* @name: used to identify the instance
|
||||
* @pcpumask: cpumask that will be used for padata parallelization
|
||||
* @cbcpumask: cpumask that will be used for padata serialization
|
||||
*
|
||||
* Must be called from a cpus_read_lock() protected region
|
||||
*/
|
||||
static struct padata_instance *padata_alloc(struct workqueue_struct *wq,
|
||||
static struct padata_instance *padata_alloc(const char *name,
|
||||
const struct cpumask *pcpumask,
|
||||
const struct cpumask *cbcpumask)
|
||||
{
|
||||
|
|
@ -1033,11 +971,23 @@ static struct padata_instance *padata_alloc(struct workqueue_struct *wq,
|
|||
if (!pinst)
|
||||
goto err;
|
||||
|
||||
if (!alloc_cpumask_var(&pinst->cpumask.pcpu, GFP_KERNEL))
|
||||
pinst->parallel_wq = alloc_workqueue("%s_parallel", WQ_UNBOUND, 0,
|
||||
name);
|
||||
if (!pinst->parallel_wq)
|
||||
goto err_free_inst;
|
||||
|
||||
get_online_cpus();
|
||||
|
||||
pinst->serial_wq = alloc_workqueue("%s_serial", WQ_MEM_RECLAIM |
|
||||
WQ_CPU_INTENSIVE, 1, name);
|
||||
if (!pinst->serial_wq)
|
||||
goto err_put_cpus;
|
||||
|
||||
if (!alloc_cpumask_var(&pinst->cpumask.pcpu, GFP_KERNEL))
|
||||
goto err_free_serial_wq;
|
||||
if (!alloc_cpumask_var(&pinst->cpumask.cbcpu, GFP_KERNEL)) {
|
||||
free_cpumask_var(pinst->cpumask.pcpu);
|
||||
goto err_free_inst;
|
||||
goto err_free_serial_wq;
|
||||
}
|
||||
if (!padata_validate_cpumask(pinst, pcpumask) ||
|
||||
!padata_validate_cpumask(pinst, cbcpumask))
|
||||
|
|
@ -1049,8 +999,6 @@ static struct padata_instance *padata_alloc(struct workqueue_struct *wq,
|
|||
|
||||
rcu_assign_pointer(pinst->pd, pd);
|
||||
|
||||
pinst->wq = wq;
|
||||
|
||||
cpumask_copy(pinst->cpumask.pcpu, pcpumask);
|
||||
cpumask_copy(pinst->cpumask.cbcpu, cbcpumask);
|
||||
|
||||
|
|
@ -1063,11 +1011,19 @@ static struct padata_instance *padata_alloc(struct workqueue_struct *wq,
|
|||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
cpuhp_state_add_instance_nocalls_cpuslocked(hp_online, &pinst->node);
|
||||
#endif
|
||||
|
||||
put_online_cpus();
|
||||
|
||||
return pinst;
|
||||
|
||||
err_free_masks:
|
||||
free_cpumask_var(pinst->cpumask.pcpu);
|
||||
free_cpumask_var(pinst->cpumask.cbcpu);
|
||||
err_free_serial_wq:
|
||||
destroy_workqueue(pinst->serial_wq);
|
||||
err_put_cpus:
|
||||
put_online_cpus();
|
||||
destroy_workqueue(pinst->parallel_wq);
|
||||
err_free_inst:
|
||||
kfree(pinst);
|
||||
err:
|
||||
|
|
@ -1079,14 +1035,11 @@ err:
|
|||
* Use the cpu_possible_mask for serial and
|
||||
* parallel workers.
|
||||
*
|
||||
* @wq: workqueue to use for the allocated padata instance
|
||||
*
|
||||
* Must be called from a cpus_read_lock() protected region
|
||||
* @name: used to identify the instance
|
||||
*/
|
||||
struct padata_instance *padata_alloc_possible(struct workqueue_struct *wq)
|
||||
struct padata_instance *padata_alloc_possible(const char *name)
|
||||
{
|
||||
lockdep_assert_cpus_held();
|
||||
return padata_alloc(wq, cpu_possible_mask, cpu_possible_mask);
|
||||
return padata_alloc(name, cpu_possible_mask, cpu_possible_mask);
|
||||
}
|
||||
EXPORT_SYMBOL(padata_alloc_possible);
|
||||
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ int pm_autosleep_set_state(suspend_state_t state)
|
|||
|
||||
int __init pm_autosleep_init(void)
|
||||
{
|
||||
autosleep_ws = wakeup_source_register("autosleep");
|
||||
autosleep_ws = wakeup_source_register(NULL, "autosleep");
|
||||
if (!autosleep_ws)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
|
|||
|
|
@ -254,7 +254,6 @@ static ssize_t pm_test_store(struct kobject *kobj, struct kobj_attribute *attr,
|
|||
power_attr(pm_test);
|
||||
#endif /* CONFIG_PM_SLEEP_DEBUG */
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
static char *suspend_step_name(enum suspend_stat_step step)
|
||||
{
|
||||
switch (step) {
|
||||
|
|
@ -275,6 +274,92 @@ static char *suspend_step_name(enum suspend_stat_step step)
|
|||
}
|
||||
}
|
||||
|
||||
#define suspend_attr(_name) \
|
||||
static ssize_t _name##_show(struct kobject *kobj, \
|
||||
struct kobj_attribute *attr, char *buf) \
|
||||
{ \
|
||||
return sprintf(buf, "%d\n", suspend_stats._name); \
|
||||
} \
|
||||
static struct kobj_attribute _name = __ATTR_RO(_name)
|
||||
|
||||
suspend_attr(success);
|
||||
suspend_attr(fail);
|
||||
suspend_attr(failed_freeze);
|
||||
suspend_attr(failed_prepare);
|
||||
suspend_attr(failed_suspend);
|
||||
suspend_attr(failed_suspend_late);
|
||||
suspend_attr(failed_suspend_noirq);
|
||||
suspend_attr(failed_resume);
|
||||
suspend_attr(failed_resume_early);
|
||||
suspend_attr(failed_resume_noirq);
|
||||
|
||||
static ssize_t last_failed_dev_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
int index;
|
||||
char *last_failed_dev = NULL;
|
||||
|
||||
index = suspend_stats.last_failed_dev + REC_FAILED_NUM - 1;
|
||||
index %= REC_FAILED_NUM;
|
||||
last_failed_dev = suspend_stats.failed_devs[index];
|
||||
|
||||
return sprintf(buf, "%s\n", last_failed_dev);
|
||||
}
|
||||
static struct kobj_attribute last_failed_dev = __ATTR_RO(last_failed_dev);
|
||||
|
||||
static ssize_t last_failed_errno_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
int index;
|
||||
int last_failed_errno;
|
||||
|
||||
index = suspend_stats.last_failed_errno + REC_FAILED_NUM - 1;
|
||||
index %= REC_FAILED_NUM;
|
||||
last_failed_errno = suspend_stats.errno[index];
|
||||
|
||||
return sprintf(buf, "%d\n", last_failed_errno);
|
||||
}
|
||||
static struct kobj_attribute last_failed_errno = __ATTR_RO(last_failed_errno);
|
||||
|
||||
static ssize_t last_failed_step_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
int index;
|
||||
enum suspend_stat_step step;
|
||||
char *last_failed_step = NULL;
|
||||
|
||||
index = suspend_stats.last_failed_step + REC_FAILED_NUM - 1;
|
||||
index %= REC_FAILED_NUM;
|
||||
step = suspend_stats.failed_steps[index];
|
||||
last_failed_step = suspend_step_name(step);
|
||||
|
||||
return sprintf(buf, "%s\n", last_failed_step);
|
||||
}
|
||||
static struct kobj_attribute last_failed_step = __ATTR_RO(last_failed_step);
|
||||
|
||||
static struct attribute *suspend_attrs[] = {
|
||||
&success.attr,
|
||||
&fail.attr,
|
||||
&failed_freeze.attr,
|
||||
&failed_prepare.attr,
|
||||
&failed_suspend.attr,
|
||||
&failed_suspend_late.attr,
|
||||
&failed_suspend_noirq.attr,
|
||||
&failed_resume.attr,
|
||||
&failed_resume_early.attr,
|
||||
&failed_resume_noirq.attr,
|
||||
&last_failed_dev.attr,
|
||||
&last_failed_errno.attr,
|
||||
&last_failed_step.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group suspend_attr_group = {
|
||||
.name = "suspend_stats",
|
||||
.attrs = suspend_attrs,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
static int suspend_stats_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
int i, index, last_dev, last_errno, last_step;
|
||||
|
|
@ -495,7 +580,7 @@ static suspend_state_t decode_state(const char *buf, size_t n)
|
|||
len = p ? p - buf : n;
|
||||
|
||||
/* Check hibernation first. */
|
||||
if (len == 4 && !strncmp(buf, "disk", len))
|
||||
if (len == 4 && str_has_prefix(buf, "disk"))
|
||||
return PM_SUSPEND_MAX;
|
||||
|
||||
#ifdef CONFIG_SUSPEND
|
||||
|
|
@ -794,6 +879,14 @@ static const struct attribute_group attr_group = {
|
|||
.attrs = g,
|
||||
};
|
||||
|
||||
static const struct attribute_group *attr_groups[] = {
|
||||
&attr_group,
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
&suspend_attr_group,
|
||||
#endif
|
||||
NULL,
|
||||
};
|
||||
|
||||
struct workqueue_struct *pm_wq;
|
||||
EXPORT_SYMBOL_GPL(pm_wq);
|
||||
|
||||
|
|
@ -815,7 +908,7 @@ static int __init pm_init(void)
|
|||
power_kobj = kobject_create_and_add("power", NULL);
|
||||
if (!power_kobj)
|
||||
return -ENOMEM;
|
||||
error = sysfs_create_group(power_kobj, &attr_group);
|
||||
error = sysfs_create_groups(power_kobj, attr_groups);
|
||||
if (error)
|
||||
return error;
|
||||
pm_print_times_init();
|
||||
|
|
|
|||
|
|
@ -78,57 +78,9 @@ static struct pm_qos_object cpu_dma_pm_qos = {
|
|||
.name = "cpu_dma_latency",
|
||||
};
|
||||
|
||||
static BLOCKING_NOTIFIER_HEAD(network_lat_notifier);
|
||||
static struct pm_qos_constraints network_lat_constraints = {
|
||||
.list = PLIST_HEAD_INIT(network_lat_constraints.list),
|
||||
.target_value = PM_QOS_NETWORK_LAT_DEFAULT_VALUE,
|
||||
.default_value = PM_QOS_NETWORK_LAT_DEFAULT_VALUE,
|
||||
.no_constraint_value = PM_QOS_NETWORK_LAT_DEFAULT_VALUE,
|
||||
.type = PM_QOS_MIN,
|
||||
.notifiers = &network_lat_notifier,
|
||||
};
|
||||
static struct pm_qos_object network_lat_pm_qos = {
|
||||
.constraints = &network_lat_constraints,
|
||||
.name = "network_latency",
|
||||
};
|
||||
|
||||
|
||||
static BLOCKING_NOTIFIER_HEAD(network_throughput_notifier);
|
||||
static struct pm_qos_constraints network_tput_constraints = {
|
||||
.list = PLIST_HEAD_INIT(network_tput_constraints.list),
|
||||
.target_value = PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE,
|
||||
.default_value = PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE,
|
||||
.no_constraint_value = PM_QOS_NETWORK_THROUGHPUT_DEFAULT_VALUE,
|
||||
.type = PM_QOS_MAX,
|
||||
.notifiers = &network_throughput_notifier,
|
||||
};
|
||||
static struct pm_qos_object network_throughput_pm_qos = {
|
||||
.constraints = &network_tput_constraints,
|
||||
.name = "network_throughput",
|
||||
};
|
||||
|
||||
|
||||
static BLOCKING_NOTIFIER_HEAD(memory_bandwidth_notifier);
|
||||
static struct pm_qos_constraints memory_bw_constraints = {
|
||||
.list = PLIST_HEAD_INIT(memory_bw_constraints.list),
|
||||
.target_value = PM_QOS_MEMORY_BANDWIDTH_DEFAULT_VALUE,
|
||||
.default_value = PM_QOS_MEMORY_BANDWIDTH_DEFAULT_VALUE,
|
||||
.no_constraint_value = PM_QOS_MEMORY_BANDWIDTH_DEFAULT_VALUE,
|
||||
.type = PM_QOS_SUM,
|
||||
.notifiers = &memory_bandwidth_notifier,
|
||||
};
|
||||
static struct pm_qos_object memory_bandwidth_pm_qos = {
|
||||
.constraints = &memory_bw_constraints,
|
||||
.name = "memory_bandwidth",
|
||||
};
|
||||
|
||||
|
||||
static struct pm_qos_object *pm_qos_array[] = {
|
||||
&null_pm_qos,
|
||||
&cpu_dma_pm_qos,
|
||||
&network_lat_pm_qos,
|
||||
&network_throughput_pm_qos,
|
||||
&memory_bandwidth_pm_qos,
|
||||
};
|
||||
|
||||
static ssize_t pm_qos_power_write(struct file *filp, const char __user *buf,
|
||||
|
|
|
|||
|
|
@ -122,43 +122,25 @@ static void s2idle_loop(void)
|
|||
{
|
||||
pm_pr_dbg("suspend-to-idle\n");
|
||||
|
||||
/*
|
||||
* Suspend-to-idle equals:
|
||||
* frozen processes + suspended devices + idle processors.
|
||||
* Thus s2idle_enter() should be called right after all devices have
|
||||
* been suspended.
|
||||
*
|
||||
* Wakeups during the noirq suspend of devices may be spurious, so try
|
||||
* to avoid them upfront.
|
||||
*/
|
||||
for (;;) {
|
||||
int error;
|
||||
|
||||
dpm_noirq_begin();
|
||||
|
||||
/*
|
||||
* Suspend-to-idle equals
|
||||
* frozen processes + suspended devices + idle processors.
|
||||
* Thus s2idle_enter() should be called right after
|
||||
* all devices have been suspended.
|
||||
*
|
||||
* Wakeups during the noirq suspend of devices may be spurious,
|
||||
* so prevent them from terminating the loop right away.
|
||||
*/
|
||||
error = dpm_noirq_suspend_devices(PMSG_SUSPEND);
|
||||
if (!error)
|
||||
s2idle_enter();
|
||||
else if (error == -EBUSY && pm_wakeup_pending())
|
||||
error = 0;
|
||||
|
||||
if (!error && s2idle_ops && s2idle_ops->wake)
|
||||
if (s2idle_ops && s2idle_ops->wake)
|
||||
s2idle_ops->wake();
|
||||
|
||||
dpm_noirq_resume_devices(PMSG_RESUME);
|
||||
|
||||
dpm_noirq_end();
|
||||
|
||||
if (error)
|
||||
break;
|
||||
|
||||
if (s2idle_ops && s2idle_ops->sync)
|
||||
s2idle_ops->sync();
|
||||
|
||||
if (pm_wakeup_pending())
|
||||
break;
|
||||
|
||||
pm_wakeup_clear(false);
|
||||
|
||||
s2idle_enter();
|
||||
}
|
||||
|
||||
pm_pr_dbg("resume from suspend-to-idle\n");
|
||||
|
|
@ -272,14 +254,21 @@ static int platform_suspend_prepare_late(suspend_state_t state)
|
|||
|
||||
static int platform_suspend_prepare_noirq(suspend_state_t state)
|
||||
{
|
||||
return state != PM_SUSPEND_TO_IDLE && suspend_ops->prepare_late ?
|
||||
suspend_ops->prepare_late() : 0;
|
||||
if (state == PM_SUSPEND_TO_IDLE)
|
||||
return s2idle_ops && s2idle_ops->prepare_late ?
|
||||
s2idle_ops->prepare_late() : 0;
|
||||
|
||||
return suspend_ops->prepare_late ? suspend_ops->prepare_late() : 0;
|
||||
}
|
||||
|
||||
static void platform_resume_noirq(suspend_state_t state)
|
||||
{
|
||||
if (state != PM_SUSPEND_TO_IDLE && suspend_ops->wake)
|
||||
if (state == PM_SUSPEND_TO_IDLE) {
|
||||
if (s2idle_ops && s2idle_ops->restore_early)
|
||||
s2idle_ops->restore_early();
|
||||
} else if (suspend_ops->wake) {
|
||||
suspend_ops->wake();
|
||||
}
|
||||
}
|
||||
|
||||
static void platform_resume_early(suspend_state_t state)
|
||||
|
|
@ -421,11 +410,6 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
|
|||
if (error)
|
||||
goto Devices_early_resume;
|
||||
|
||||
if (state == PM_SUSPEND_TO_IDLE && pm_test_level != TEST_PLATFORM) {
|
||||
s2idle_loop();
|
||||
goto Platform_early_resume;
|
||||
}
|
||||
|
||||
error = dpm_suspend_noirq(PMSG_SUSPEND);
|
||||
if (error) {
|
||||
last_dev = suspend_stats.last_failed_dev + REC_FAILED_NUM - 1;
|
||||
|
|
@ -442,6 +426,11 @@ static int suspend_enter(suspend_state_t state, bool *wakeup)
|
|||
if (suspend_test(TEST_PLATFORM))
|
||||
goto Platform_wake;
|
||||
|
||||
if (state == PM_SUSPEND_TO_IDLE) {
|
||||
s2idle_loop();
|
||||
goto Platform_wake;
|
||||
}
|
||||
|
||||
error = suspend_disable_secondary_cpus();
|
||||
if (error || suspend_test(TEST_CPUS)) {
|
||||
log_suspend_abort_reason("Disabling non-boot cpus failed");
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ static DEFINE_MUTEX(wakelocks_lock);
|
|||
struct wakelock {
|
||||
char *name;
|
||||
struct rb_node node;
|
||||
struct wakeup_source ws;
|
||||
struct wakeup_source *ws;
|
||||
#ifdef CONFIG_PM_WAKELOCKS_GC
|
||||
struct list_head lru;
|
||||
#endif
|
||||
|
|
@ -46,7 +46,7 @@ ssize_t pm_show_wakelocks(char *buf, bool show_active)
|
|||
|
||||
for (node = rb_first(&wakelocks_tree); node; node = rb_next(node)) {
|
||||
wl = rb_entry(node, struct wakelock, node);
|
||||
if (wl->ws.active == show_active)
|
||||
if (wl->ws->active == show_active)
|
||||
str += scnprintf(str, end - str, "%s ", wl->name);
|
||||
}
|
||||
if (str > buf)
|
||||
|
|
@ -112,16 +112,16 @@ static void __wakelocks_gc(struct work_struct *work)
|
|||
u64 idle_time_ns;
|
||||
bool active;
|
||||
|
||||
spin_lock_irq(&wl->ws.lock);
|
||||
idle_time_ns = ktime_to_ns(ktime_sub(now, wl->ws.last_time));
|
||||
active = wl->ws.active;
|
||||
spin_unlock_irq(&wl->ws.lock);
|
||||
spin_lock_irq(&wl->ws->lock);
|
||||
idle_time_ns = ktime_to_ns(ktime_sub(now, wl->ws->last_time));
|
||||
active = wl->ws->active;
|
||||
spin_unlock_irq(&wl->ws->lock);
|
||||
|
||||
if (idle_time_ns < ((u64)WL_GC_TIME_SEC * NSEC_PER_SEC))
|
||||
break;
|
||||
|
||||
if (!active) {
|
||||
wakeup_source_remove(&wl->ws);
|
||||
wakeup_source_unregister(wl->ws);
|
||||
rb_erase(&wl->node, &wakelocks_tree);
|
||||
list_del(&wl->lru);
|
||||
kfree(wl->name);
|
||||
|
|
@ -187,9 +187,15 @@ static struct wakelock *wakelock_lookup_add(const char *name, size_t len,
|
|||
kfree(wl);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
wl->ws.name = wl->name;
|
||||
wl->ws.last_time = ktime_get();
|
||||
wakeup_source_add(&wl->ws);
|
||||
|
||||
wl->ws = wakeup_source_register(NULL, wl->name);
|
||||
if (!wl->ws) {
|
||||
kfree(wl->name);
|
||||
kfree(wl);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
wl->ws->last_time = ktime_get();
|
||||
|
||||
rb_link_node(&wl->node, parent, node);
|
||||
rb_insert_color(&wl->node, &wakelocks_tree);
|
||||
wakelocks_lru_add(wl);
|
||||
|
|
@ -233,9 +239,9 @@ int pm_wake_lock(const char *buf)
|
|||
u64 timeout_ms = timeout_ns + NSEC_PER_MSEC - 1;
|
||||
|
||||
do_div(timeout_ms, NSEC_PER_MSEC);
|
||||
__pm_wakeup_event(&wl->ws, timeout_ms);
|
||||
__pm_wakeup_event(wl->ws, timeout_ms);
|
||||
} else {
|
||||
__pm_stay_awake(&wl->ws);
|
||||
__pm_stay_awake(wl->ws);
|
||||
}
|
||||
|
||||
wakelocks_lru_most_recent(wl);
|
||||
|
|
@ -271,7 +277,7 @@ int pm_wake_unlock(const char *buf)
|
|||
ret = PTR_ERR(wl);
|
||||
goto out;
|
||||
}
|
||||
__pm_relax(&wl->ws);
|
||||
__pm_relax(wl->ws);
|
||||
|
||||
wakelocks_lru_most_recent(wl);
|
||||
wakelocks_gc();
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ menu "RCU Subsystem"
|
|||
|
||||
config TREE_RCU
|
||||
bool
|
||||
default y if !PREEMPT && SMP
|
||||
default y if !PREEMPTION && SMP
|
||||
help
|
||||
This option selects the RCU implementation that is
|
||||
designed for very large SMP system with hundreds or
|
||||
|
|
@ -16,7 +16,7 @@ config TREE_RCU
|
|||
|
||||
config PREEMPT_RCU
|
||||
bool
|
||||
default y if PREEMPT
|
||||
default y if PREEMPTION
|
||||
help
|
||||
This option selects the RCU implementation that is
|
||||
designed for very large SMP systems with hundreds or
|
||||
|
|
@ -28,7 +28,7 @@ config PREEMPT_RCU
|
|||
|
||||
config TINY_RCU
|
||||
bool
|
||||
default y if !PREEMPT && !SMP
|
||||
default y if !PREEMPTION && !SMP
|
||||
help
|
||||
This option selects the RCU implementation that is
|
||||
designed for UP systems from which real-time response
|
||||
|
|
@ -70,7 +70,7 @@ config TREE_SRCU
|
|||
This option selects the full-fledged version of SRCU.
|
||||
|
||||
config TASKS_RCU
|
||||
def_bool PREEMPT
|
||||
def_bool PREEMPTION
|
||||
select SRCU
|
||||
help
|
||||
This option enables a task-based RCU implementation that uses
|
||||
|
|
|
|||
|
|
@ -8,6 +8,17 @@ menu "RCU Debugging"
|
|||
config PROVE_RCU
|
||||
def_bool PROVE_LOCKING
|
||||
|
||||
config PROVE_RCU_LIST
|
||||
bool "RCU list lockdep debugging"
|
||||
depends on PROVE_RCU && RCU_EXPERT
|
||||
default n
|
||||
help
|
||||
Enable RCU lockdep checking for list usages. By default it is
|
||||
turned off since there are several list RCU users that still
|
||||
need to be converted to pass a lockdep expression. To prevent
|
||||
false-positive splats, we keep it default disabled but once all
|
||||
users are converted, we can remove this config option.
|
||||
|
||||
config TORTURE_TEST
|
||||
tristate
|
||||
default n
|
||||
|
|
|
|||
|
|
@ -227,6 +227,7 @@ static inline bool __rcu_reclaim(const char *rn, struct rcu_head *head)
|
|||
|
||||
#ifdef CONFIG_RCU_STALL_COMMON
|
||||
|
||||
extern int rcu_cpu_stall_ftrace_dump;
|
||||
extern int rcu_cpu_stall_suppress;
|
||||
extern int rcu_cpu_stall_timeout;
|
||||
int rcu_jiffies_till_stall_check(void);
|
||||
|
|
|
|||
|
|
@ -23,6 +23,49 @@ void rcu_cblist_init(struct rcu_cblist *rclp)
|
|||
rclp->len_lazy = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enqueue an rcu_head structure onto the specified callback list.
|
||||
* This function assumes that the callback is non-lazy because it
|
||||
* is intended for use by no-CBs CPUs, which do not distinguish
|
||||
* between lazy and non-lazy RCU callbacks.
|
||||
*/
|
||||
void rcu_cblist_enqueue(struct rcu_cblist *rclp, struct rcu_head *rhp)
|
||||
{
|
||||
*rclp->tail = rhp;
|
||||
rclp->tail = &rhp->next;
|
||||
WRITE_ONCE(rclp->len, rclp->len + 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Flush the second rcu_cblist structure onto the first one, obliterating
|
||||
* any contents of the first. If rhp is non-NULL, enqueue it as the sole
|
||||
* element of the second rcu_cblist structure, but ensuring that the second
|
||||
* rcu_cblist structure, if initially non-empty, always appears non-empty
|
||||
* throughout the process. If rdp is NULL, the second rcu_cblist structure
|
||||
* is instead initialized to empty.
|
||||
*/
|
||||
void rcu_cblist_flush_enqueue(struct rcu_cblist *drclp,
|
||||
struct rcu_cblist *srclp,
|
||||
struct rcu_head *rhp)
|
||||
{
|
||||
drclp->head = srclp->head;
|
||||
if (drclp->head)
|
||||
drclp->tail = srclp->tail;
|
||||
else
|
||||
drclp->tail = &drclp->head;
|
||||
drclp->len = srclp->len;
|
||||
drclp->len_lazy = srclp->len_lazy;
|
||||
if (!rhp) {
|
||||
rcu_cblist_init(srclp);
|
||||
} else {
|
||||
rhp->next = NULL;
|
||||
srclp->head = rhp;
|
||||
srclp->tail = &rhp->next;
|
||||
WRITE_ONCE(srclp->len, 1);
|
||||
srclp->len_lazy = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Dequeue the oldest rcu_head structure from the specified callback
|
||||
* list. This function assumes that the callback is non-lazy, but
|
||||
|
|
@ -44,6 +87,67 @@ struct rcu_head *rcu_cblist_dequeue(struct rcu_cblist *rclp)
|
|||
return rhp;
|
||||
}
|
||||
|
||||
/* Set the length of an rcu_segcblist structure. */
|
||||
void rcu_segcblist_set_len(struct rcu_segcblist *rsclp, long v)
|
||||
{
|
||||
#ifdef CONFIG_RCU_NOCB_CPU
|
||||
atomic_long_set(&rsclp->len, v);
|
||||
#else
|
||||
WRITE_ONCE(rsclp->len, v);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Increase the numeric length of an rcu_segcblist structure by the
|
||||
* specified amount, which can be negative. This can cause the ->len
|
||||
* field to disagree with the actual number of callbacks on the structure.
|
||||
* This increase is fully ordered with respect to the callers accesses
|
||||
* both before and after.
|
||||
*/
|
||||
void rcu_segcblist_add_len(struct rcu_segcblist *rsclp, long v)
|
||||
{
|
||||
#ifdef CONFIG_RCU_NOCB_CPU
|
||||
smp_mb__before_atomic(); /* Up to the caller! */
|
||||
atomic_long_add(v, &rsclp->len);
|
||||
smp_mb__after_atomic(); /* Up to the caller! */
|
||||
#else
|
||||
smp_mb(); /* Up to the caller! */
|
||||
WRITE_ONCE(rsclp->len, rsclp->len + v);
|
||||
smp_mb(); /* Up to the caller! */
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Increase the numeric length of an rcu_segcblist structure by one.
|
||||
* This can cause the ->len field to disagree with the actual number of
|
||||
* callbacks on the structure. This increase is fully ordered with respect
|
||||
* to the callers accesses both before and after.
|
||||
*/
|
||||
void rcu_segcblist_inc_len(struct rcu_segcblist *rsclp)
|
||||
{
|
||||
rcu_segcblist_add_len(rsclp, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Exchange the numeric length of the specified rcu_segcblist structure
|
||||
* with the specified value. This can cause the ->len field to disagree
|
||||
* with the actual number of callbacks on the structure. This exchange is
|
||||
* fully ordered with respect to the callers accesses both before and after.
|
||||
*/
|
||||
long rcu_segcblist_xchg_len(struct rcu_segcblist *rsclp, long v)
|
||||
{
|
||||
#ifdef CONFIG_RCU_NOCB_CPU
|
||||
return atomic_long_xchg(&rsclp->len, v);
|
||||
#else
|
||||
long ret = rsclp->len;
|
||||
|
||||
smp_mb(); /* Up to the caller! */
|
||||
WRITE_ONCE(rsclp->len, v);
|
||||
smp_mb(); /* Up to the caller! */
|
||||
return ret;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize an rcu_segcblist structure.
|
||||
*/
|
||||
|
|
@ -56,8 +160,9 @@ void rcu_segcblist_init(struct rcu_segcblist *rsclp)
|
|||
rsclp->head = NULL;
|
||||
for (i = 0; i < RCU_CBLIST_NSEGS; i++)
|
||||
rsclp->tails[i] = &rsclp->head;
|
||||
rsclp->len = 0;
|
||||
rcu_segcblist_set_len(rsclp, 0);
|
||||
rsclp->len_lazy = 0;
|
||||
rsclp->enabled = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -69,7 +174,16 @@ void rcu_segcblist_disable(struct rcu_segcblist *rsclp)
|
|||
WARN_ON_ONCE(!rcu_segcblist_empty(rsclp));
|
||||
WARN_ON_ONCE(rcu_segcblist_n_cbs(rsclp));
|
||||
WARN_ON_ONCE(rcu_segcblist_n_lazy_cbs(rsclp));
|
||||
rsclp->tails[RCU_NEXT_TAIL] = NULL;
|
||||
rsclp->enabled = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Mark the specified rcu_segcblist structure as offloaded. This
|
||||
* structure must be empty.
|
||||
*/
|
||||
void rcu_segcblist_offload(struct rcu_segcblist *rsclp)
|
||||
{
|
||||
rsclp->offloaded = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -117,6 +231,18 @@ struct rcu_head *rcu_segcblist_first_pend_cb(struct rcu_segcblist *rsclp)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return false if there are no CBs awaiting grace periods, otherwise,
|
||||
* return true and store the nearest waited-upon grace period into *lp.
|
||||
*/
|
||||
bool rcu_segcblist_nextgp(struct rcu_segcblist *rsclp, unsigned long *lp)
|
||||
{
|
||||
if (!rcu_segcblist_pend_cbs(rsclp))
|
||||
return false;
|
||||
*lp = rsclp->gp_seq[RCU_WAIT_TAIL];
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enqueue the specified callback onto the specified rcu_segcblist
|
||||
* structure, updating accounting as needed. Note that the ->len
|
||||
|
|
@ -129,13 +255,13 @@ struct rcu_head *rcu_segcblist_first_pend_cb(struct rcu_segcblist *rsclp)
|
|||
void rcu_segcblist_enqueue(struct rcu_segcblist *rsclp,
|
||||
struct rcu_head *rhp, bool lazy)
|
||||
{
|
||||
WRITE_ONCE(rsclp->len, rsclp->len + 1); /* ->len sampled locklessly. */
|
||||
rcu_segcblist_inc_len(rsclp);
|
||||
if (lazy)
|
||||
rsclp->len_lazy++;
|
||||
smp_mb(); /* Ensure counts are updated before callback is enqueued. */
|
||||
rhp->next = NULL;
|
||||
*rsclp->tails[RCU_NEXT_TAIL] = rhp;
|
||||
rsclp->tails[RCU_NEXT_TAIL] = &rhp->next;
|
||||
WRITE_ONCE(*rsclp->tails[RCU_NEXT_TAIL], rhp);
|
||||
WRITE_ONCE(rsclp->tails[RCU_NEXT_TAIL], &rhp->next);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -155,7 +281,7 @@ bool rcu_segcblist_entrain(struct rcu_segcblist *rsclp,
|
|||
|
||||
if (rcu_segcblist_n_cbs(rsclp) == 0)
|
||||
return false;
|
||||
WRITE_ONCE(rsclp->len, rsclp->len + 1);
|
||||
rcu_segcblist_inc_len(rsclp);
|
||||
if (lazy)
|
||||
rsclp->len_lazy++;
|
||||
smp_mb(); /* Ensure counts are updated before callback is entrained. */
|
||||
|
|
@ -163,9 +289,9 @@ bool rcu_segcblist_entrain(struct rcu_segcblist *rsclp,
|
|||
for (i = RCU_NEXT_TAIL; i > RCU_DONE_TAIL; i--)
|
||||
if (rsclp->tails[i] != rsclp->tails[i - 1])
|
||||
break;
|
||||
*rsclp->tails[i] = rhp;
|
||||
WRITE_ONCE(*rsclp->tails[i], rhp);
|
||||
for (; i <= RCU_NEXT_TAIL; i++)
|
||||
rsclp->tails[i] = &rhp->next;
|
||||
WRITE_ONCE(rsclp->tails[i], &rhp->next);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -182,9 +308,8 @@ void rcu_segcblist_extract_count(struct rcu_segcblist *rsclp,
|
|||
struct rcu_cblist *rclp)
|
||||
{
|
||||
rclp->len_lazy += rsclp->len_lazy;
|
||||
rclp->len += rsclp->len;
|
||||
rsclp->len_lazy = 0;
|
||||
WRITE_ONCE(rsclp->len, 0); /* ->len sampled locklessly. */
|
||||
rclp->len = rcu_segcblist_xchg_len(rsclp, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -200,12 +325,12 @@ void rcu_segcblist_extract_done_cbs(struct rcu_segcblist *rsclp,
|
|||
if (!rcu_segcblist_ready_cbs(rsclp))
|
||||
return; /* Nothing to do. */
|
||||
*rclp->tail = rsclp->head;
|
||||
rsclp->head = *rsclp->tails[RCU_DONE_TAIL];
|
||||
*rsclp->tails[RCU_DONE_TAIL] = NULL;
|
||||
WRITE_ONCE(rsclp->head, *rsclp->tails[RCU_DONE_TAIL]);
|
||||
WRITE_ONCE(*rsclp->tails[RCU_DONE_TAIL], NULL);
|
||||
rclp->tail = rsclp->tails[RCU_DONE_TAIL];
|
||||
for (i = RCU_CBLIST_NSEGS - 1; i >= RCU_DONE_TAIL; i--)
|
||||
if (rsclp->tails[i] == rsclp->tails[RCU_DONE_TAIL])
|
||||
rsclp->tails[i] = &rsclp->head;
|
||||
WRITE_ONCE(rsclp->tails[i], &rsclp->head);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -224,9 +349,9 @@ void rcu_segcblist_extract_pend_cbs(struct rcu_segcblist *rsclp,
|
|||
return; /* Nothing to do. */
|
||||
*rclp->tail = *rsclp->tails[RCU_DONE_TAIL];
|
||||
rclp->tail = rsclp->tails[RCU_NEXT_TAIL];
|
||||
*rsclp->tails[RCU_DONE_TAIL] = NULL;
|
||||
WRITE_ONCE(*rsclp->tails[RCU_DONE_TAIL], NULL);
|
||||
for (i = RCU_DONE_TAIL + 1; i < RCU_CBLIST_NSEGS; i++)
|
||||
rsclp->tails[i] = rsclp->tails[RCU_DONE_TAIL];
|
||||
WRITE_ONCE(rsclp->tails[i], rsclp->tails[RCU_DONE_TAIL]);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -237,8 +362,7 @@ void rcu_segcblist_insert_count(struct rcu_segcblist *rsclp,
|
|||
struct rcu_cblist *rclp)
|
||||
{
|
||||
rsclp->len_lazy += rclp->len_lazy;
|
||||
/* ->len sampled locklessly. */
|
||||
WRITE_ONCE(rsclp->len, rsclp->len + rclp->len);
|
||||
rcu_segcblist_add_len(rsclp, rclp->len);
|
||||
rclp->len_lazy = 0;
|
||||
rclp->len = 0;
|
||||
}
|
||||
|
|
@ -255,10 +379,10 @@ void rcu_segcblist_insert_done_cbs(struct rcu_segcblist *rsclp,
|
|||
if (!rclp->head)
|
||||
return; /* No callbacks to move. */
|
||||
*rclp->tail = rsclp->head;
|
||||
rsclp->head = rclp->head;
|
||||
WRITE_ONCE(rsclp->head, rclp->head);
|
||||
for (i = RCU_DONE_TAIL; i < RCU_CBLIST_NSEGS; i++)
|
||||
if (&rsclp->head == rsclp->tails[i])
|
||||
rsclp->tails[i] = rclp->tail;
|
||||
WRITE_ONCE(rsclp->tails[i], rclp->tail);
|
||||
else
|
||||
break;
|
||||
rclp->head = NULL;
|
||||
|
|
@ -274,8 +398,8 @@ void rcu_segcblist_insert_pend_cbs(struct rcu_segcblist *rsclp,
|
|||
{
|
||||
if (!rclp->head)
|
||||
return; /* Nothing to do. */
|
||||
*rsclp->tails[RCU_NEXT_TAIL] = rclp->head;
|
||||
rsclp->tails[RCU_NEXT_TAIL] = rclp->tail;
|
||||
WRITE_ONCE(*rsclp->tails[RCU_NEXT_TAIL], rclp->head);
|
||||
WRITE_ONCE(rsclp->tails[RCU_NEXT_TAIL], rclp->tail);
|
||||
rclp->head = NULL;
|
||||
rclp->tail = &rclp->head;
|
||||
}
|
||||
|
|
@ -299,7 +423,7 @@ void rcu_segcblist_advance(struct rcu_segcblist *rsclp, unsigned long seq)
|
|||
for (i = RCU_WAIT_TAIL; i < RCU_NEXT_TAIL; i++) {
|
||||
if (ULONG_CMP_LT(seq, rsclp->gp_seq[i]))
|
||||
break;
|
||||
rsclp->tails[RCU_DONE_TAIL] = rsclp->tails[i];
|
||||
WRITE_ONCE(rsclp->tails[RCU_DONE_TAIL], rsclp->tails[i]);
|
||||
}
|
||||
|
||||
/* If no callbacks moved, nothing more need be done. */
|
||||
|
|
@ -308,7 +432,7 @@ void rcu_segcblist_advance(struct rcu_segcblist *rsclp, unsigned long seq)
|
|||
|
||||
/* Clean up tail pointers that might have been misordered above. */
|
||||
for (j = RCU_WAIT_TAIL; j < i; j++)
|
||||
rsclp->tails[j] = rsclp->tails[RCU_DONE_TAIL];
|
||||
WRITE_ONCE(rsclp->tails[j], rsclp->tails[RCU_DONE_TAIL]);
|
||||
|
||||
/*
|
||||
* Callbacks moved, so clean up the misordered ->tails[] pointers
|
||||
|
|
@ -319,7 +443,7 @@ void rcu_segcblist_advance(struct rcu_segcblist *rsclp, unsigned long seq)
|
|||
for (j = RCU_WAIT_TAIL; i < RCU_NEXT_TAIL; i++, j++) {
|
||||
if (rsclp->tails[j] == rsclp->tails[RCU_NEXT_TAIL])
|
||||
break; /* No more callbacks. */
|
||||
rsclp->tails[j] = rsclp->tails[i];
|
||||
WRITE_ONCE(rsclp->tails[j], rsclp->tails[i]);
|
||||
rsclp->gp_seq[j] = rsclp->gp_seq[i];
|
||||
}
|
||||
}
|
||||
|
|
@ -384,7 +508,7 @@ bool rcu_segcblist_accelerate(struct rcu_segcblist *rsclp, unsigned long seq)
|
|||
* structure other than in the RCU_NEXT_TAIL segment.
|
||||
*/
|
||||
for (; i < RCU_NEXT_TAIL; i++) {
|
||||
rsclp->tails[i] = rsclp->tails[RCU_NEXT_TAIL];
|
||||
WRITE_ONCE(rsclp->tails[i], rsclp->tails[RCU_NEXT_TAIL]);
|
||||
rsclp->gp_seq[i] = seq;
|
||||
}
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,12 @@
|
|||
|
||||
#include <linux/rcu_segcblist.h>
|
||||
|
||||
/* Return number of callbacks in the specified callback list. */
|
||||
static inline long rcu_cblist_n_cbs(struct rcu_cblist *rclp)
|
||||
{
|
||||
return READ_ONCE(rclp->len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Account for the fact that a previously dequeued callback turned out
|
||||
* to be marked as lazy.
|
||||
|
|
@ -19,6 +25,10 @@ static inline void rcu_cblist_dequeued_lazy(struct rcu_cblist *rclp)
|
|||
}
|
||||
|
||||
void rcu_cblist_init(struct rcu_cblist *rclp);
|
||||
void rcu_cblist_enqueue(struct rcu_cblist *rclp, struct rcu_head *rhp);
|
||||
void rcu_cblist_flush_enqueue(struct rcu_cblist *drclp,
|
||||
struct rcu_cblist *srclp,
|
||||
struct rcu_head *rhp);
|
||||
struct rcu_head *rcu_cblist_dequeue(struct rcu_cblist *rclp);
|
||||
|
||||
/*
|
||||
|
|
@ -36,13 +46,17 @@ struct rcu_head *rcu_cblist_dequeue(struct rcu_cblist *rclp);
|
|||
*/
|
||||
static inline bool rcu_segcblist_empty(struct rcu_segcblist *rsclp)
|
||||
{
|
||||
return !rsclp->head;
|
||||
return !READ_ONCE(rsclp->head);
|
||||
}
|
||||
|
||||
/* Return number of callbacks in segmented callback list. */
|
||||
static inline long rcu_segcblist_n_cbs(struct rcu_segcblist *rsclp)
|
||||
{
|
||||
#ifdef CONFIG_RCU_NOCB_CPU
|
||||
return atomic_long_read(&rsclp->len);
|
||||
#else
|
||||
return READ_ONCE(rsclp->len);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Return number of lazy callbacks in segmented callback list. */
|
||||
|
|
@ -54,16 +68,22 @@ static inline long rcu_segcblist_n_lazy_cbs(struct rcu_segcblist *rsclp)
|
|||
/* Return number of lazy callbacks in segmented callback list. */
|
||||
static inline long rcu_segcblist_n_nonlazy_cbs(struct rcu_segcblist *rsclp)
|
||||
{
|
||||
return rsclp->len - rsclp->len_lazy;
|
||||
return rcu_segcblist_n_cbs(rsclp) - rsclp->len_lazy;
|
||||
}
|
||||
|
||||
/*
|
||||
* Is the specified rcu_segcblist enabled, for example, not corresponding
|
||||
* to an offline or callback-offloaded CPU?
|
||||
* to an offline CPU?
|
||||
*/
|
||||
static inline bool rcu_segcblist_is_enabled(struct rcu_segcblist *rsclp)
|
||||
{
|
||||
return !!rsclp->tails[RCU_NEXT_TAIL];
|
||||
return rsclp->enabled;
|
||||
}
|
||||
|
||||
/* Is the specified rcu_segcblist offloaded? */
|
||||
static inline bool rcu_segcblist_is_offloaded(struct rcu_segcblist *rsclp)
|
||||
{
|
||||
return rsclp->offloaded;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -73,36 +93,18 @@ static inline bool rcu_segcblist_is_enabled(struct rcu_segcblist *rsclp)
|
|||
*/
|
||||
static inline bool rcu_segcblist_restempty(struct rcu_segcblist *rsclp, int seg)
|
||||
{
|
||||
return !*rsclp->tails[seg];
|
||||
}
|
||||
|
||||
/*
|
||||
* Interim function to return rcu_segcblist head pointer. Longer term, the
|
||||
* rcu_segcblist will be used more pervasively, removing the need for this
|
||||
* function.
|
||||
*/
|
||||
static inline struct rcu_head *rcu_segcblist_head(struct rcu_segcblist *rsclp)
|
||||
{
|
||||
return rsclp->head;
|
||||
}
|
||||
|
||||
/*
|
||||
* Interim function to return rcu_segcblist head pointer. Longer term, the
|
||||
* rcu_segcblist will be used more pervasively, removing the need for this
|
||||
* function.
|
||||
*/
|
||||
static inline struct rcu_head **rcu_segcblist_tail(struct rcu_segcblist *rsclp)
|
||||
{
|
||||
WARN_ON_ONCE(rcu_segcblist_empty(rsclp));
|
||||
return rsclp->tails[RCU_NEXT_TAIL];
|
||||
return !READ_ONCE(*READ_ONCE(rsclp->tails[seg]));
|
||||
}
|
||||
|
||||
void rcu_segcblist_inc_len(struct rcu_segcblist *rsclp);
|
||||
void rcu_segcblist_init(struct rcu_segcblist *rsclp);
|
||||
void rcu_segcblist_disable(struct rcu_segcblist *rsclp);
|
||||
void rcu_segcblist_offload(struct rcu_segcblist *rsclp);
|
||||
bool rcu_segcblist_ready_cbs(struct rcu_segcblist *rsclp);
|
||||
bool rcu_segcblist_pend_cbs(struct rcu_segcblist *rsclp);
|
||||
struct rcu_head *rcu_segcblist_first_cb(struct rcu_segcblist *rsclp);
|
||||
struct rcu_head *rcu_segcblist_first_pend_cb(struct rcu_segcblist *rsclp);
|
||||
bool rcu_segcblist_nextgp(struct rcu_segcblist *rsclp, unsigned long *lp);
|
||||
void rcu_segcblist_enqueue(struct rcu_segcblist *rsclp,
|
||||
struct rcu_head *rhp, bool lazy);
|
||||
bool rcu_segcblist_entrain(struct rcu_segcblist *rsclp,
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ torture_param(int, writer_holdoff, 0, "Holdoff (us) between GPs, zero to disable
|
|||
|
||||
static char *perf_type = "rcu";
|
||||
module_param(perf_type, charp, 0444);
|
||||
MODULE_PARM_DESC(perf_type, "Type of RCU to performance-test (rcu, rcu_bh, ...)");
|
||||
MODULE_PARM_DESC(perf_type, "Type of RCU to performance-test (rcu, srcu, ...)");
|
||||
|
||||
static int nrealreaders;
|
||||
static int nrealwriters;
|
||||
|
|
@ -375,6 +375,14 @@ rcu_perf_writer(void *arg)
|
|||
if (holdoff)
|
||||
schedule_timeout_uninterruptible(holdoff * HZ);
|
||||
|
||||
/*
|
||||
* Wait until rcu_end_inkernel_boot() is called for normal GP tests
|
||||
* so that RCU is not always expedited for normal GP tests.
|
||||
* The system_state test is approximate, but works well in practice.
|
||||
*/
|
||||
while (!gp_exp && system_state != SYSTEM_RUNNING)
|
||||
schedule_timeout_uninterruptible(1);
|
||||
|
||||
t = ktime_get_mono_fast_ns();
|
||||
if (atomic_inc_return(&n_rcu_perf_writer_started) >= nrealwriters) {
|
||||
t_rcu_perf_writer_started = t;
|
||||
|
|
|
|||
|
|
@ -161,6 +161,7 @@ static atomic_long_t n_rcu_torture_timers;
|
|||
static long n_barrier_attempts;
|
||||
static long n_barrier_successes; /* did rcu_barrier test succeed? */
|
||||
static struct list_head rcu_torture_removed;
|
||||
static unsigned long shutdown_jiffies;
|
||||
|
||||
static int rcu_torture_writer_state;
|
||||
#define RTWS_FIXED_DELAY 0
|
||||
|
|
@ -228,6 +229,15 @@ static u64 notrace rcu_trace_clock_local(void)
|
|||
}
|
||||
#endif /* #else #ifdef CONFIG_RCU_TRACE */
|
||||
|
||||
/*
|
||||
* Stop aggressive CPU-hog tests a bit before the end of the test in order
|
||||
* to avoid interfering with test shutdown.
|
||||
*/
|
||||
static bool shutdown_time_arrived(void)
|
||||
{
|
||||
return shutdown_secs && time_after(jiffies, shutdown_jiffies - 30 * HZ);
|
||||
}
|
||||
|
||||
static unsigned long boost_starttime; /* jiffies of next boost test start. */
|
||||
static DEFINE_MUTEX(boost_mutex); /* protect setting boost_starttime */
|
||||
/* and boost task create/destroy. */
|
||||
|
|
@ -1713,12 +1723,14 @@ static void rcu_torture_fwd_cb_cr(struct rcu_head *rhp)
|
|||
}
|
||||
|
||||
// Give the scheduler a chance, even on nohz_full CPUs.
|
||||
static void rcu_torture_fwd_prog_cond_resched(void)
|
||||
static void rcu_torture_fwd_prog_cond_resched(unsigned long iter)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_PREEMPT) && IS_ENABLED(CONFIG_NO_HZ_FULL)) {
|
||||
if (need_resched())
|
||||
// Real call_rcu() floods hit userspace, so emulate that.
|
||||
if (need_resched() || (iter & 0xfff))
|
||||
schedule();
|
||||
} else {
|
||||
// No userspace emulation: CB invocation throttles call_rcu()
|
||||
cond_resched();
|
||||
}
|
||||
}
|
||||
|
|
@ -1746,7 +1758,7 @@ static unsigned long rcu_torture_fwd_prog_cbfree(void)
|
|||
spin_unlock_irqrestore(&rcu_fwd_lock, flags);
|
||||
kfree(rfcp);
|
||||
freed++;
|
||||
rcu_torture_fwd_prog_cond_resched();
|
||||
rcu_torture_fwd_prog_cond_resched(freed);
|
||||
}
|
||||
return freed;
|
||||
}
|
||||
|
|
@ -1785,15 +1797,17 @@ static void rcu_torture_fwd_prog_nr(int *tested, int *tested_tries)
|
|||
WRITE_ONCE(rcu_fwd_startat, jiffies);
|
||||
stopat = rcu_fwd_startat + dur;
|
||||
while (time_before(jiffies, stopat) &&
|
||||
!shutdown_time_arrived() &&
|
||||
!READ_ONCE(rcu_fwd_emergency_stop) && !torture_must_stop()) {
|
||||
idx = cur_ops->readlock();
|
||||
udelay(10);
|
||||
cur_ops->readunlock(idx);
|
||||
if (!fwd_progress_need_resched || need_resched())
|
||||
rcu_torture_fwd_prog_cond_resched();
|
||||
rcu_torture_fwd_prog_cond_resched(1);
|
||||
}
|
||||
(*tested_tries)++;
|
||||
if (!time_before(jiffies, stopat) &&
|
||||
!shutdown_time_arrived() &&
|
||||
!READ_ONCE(rcu_fwd_emergency_stop) && !torture_must_stop()) {
|
||||
(*tested)++;
|
||||
cver = READ_ONCE(rcu_torture_current_version) - cver;
|
||||
|
|
@ -1852,6 +1866,7 @@ static void rcu_torture_fwd_prog_cr(void)
|
|||
gps = cur_ops->get_gp_seq();
|
||||
rcu_launder_gp_seq_start = gps;
|
||||
while (time_before(jiffies, stopat) &&
|
||||
!shutdown_time_arrived() &&
|
||||
!READ_ONCE(rcu_fwd_emergency_stop) && !torture_must_stop()) {
|
||||
rfcp = READ_ONCE(rcu_fwd_cb_head);
|
||||
rfcpn = NULL;
|
||||
|
|
@ -1875,7 +1890,7 @@ static void rcu_torture_fwd_prog_cr(void)
|
|||
rfcp->rfc_gps = 0;
|
||||
}
|
||||
cur_ops->call(&rfcp->rh, rcu_torture_fwd_cb_cr);
|
||||
rcu_torture_fwd_prog_cond_resched();
|
||||
rcu_torture_fwd_prog_cond_resched(n_launders + n_max_cbs);
|
||||
}
|
||||
stoppedat = jiffies;
|
||||
n_launders_cb_snap = READ_ONCE(n_launders_cb);
|
||||
|
|
@ -1884,7 +1899,8 @@ static void rcu_torture_fwd_prog_cr(void)
|
|||
cur_ops->cb_barrier(); /* Wait for callbacks to be invoked. */
|
||||
(void)rcu_torture_fwd_prog_cbfree();
|
||||
|
||||
if (!torture_must_stop() && !READ_ONCE(rcu_fwd_emergency_stop)) {
|
||||
if (!torture_must_stop() && !READ_ONCE(rcu_fwd_emergency_stop) &&
|
||||
!shutdown_time_arrived()) {
|
||||
WARN_ON(n_max_gps < MIN_FWD_CBS_LAUNDERED);
|
||||
pr_alert("%s Duration %lu barrier: %lu pending %ld n_launders: %ld n_launders_sa: %ld n_max_gps: %ld n_max_cbs: %ld cver %ld gps %ld\n",
|
||||
__func__,
|
||||
|
|
@ -2160,6 +2176,7 @@ rcu_torture_cleanup(void)
|
|||
return;
|
||||
}
|
||||
|
||||
show_rcu_gp_kthreads();
|
||||
rcu_torture_barrier_cleanup();
|
||||
torture_stop_kthread(rcu_torture_fwd_prog, fwd_prog_task);
|
||||
torture_stop_kthread(rcu_torture_stall, stall_task);
|
||||
|
|
@ -2465,6 +2482,7 @@ rcu_torture_init(void)
|
|||
goto unwind;
|
||||
rcutor_hp = firsterr;
|
||||
}
|
||||
shutdown_jiffies = jiffies + shutdown_secs * HZ;
|
||||
firsterr = torture_shutdown_init(shutdown_secs, rcu_torture_cleanup);
|
||||
if (firsterr)
|
||||
goto unwind;
|
||||
|
|
|
|||
|
|
@ -1279,8 +1279,9 @@ void srcu_torture_stats_print(struct srcu_struct *ssp, char *tt, char *tf)
|
|||
|
||||
c0 = l0 - u0;
|
||||
c1 = l1 - u1;
|
||||
pr_cont(" %d(%ld,%ld %1p)",
|
||||
cpu, c0, c1, rcu_segcblist_head(&sdp->srcu_cblist));
|
||||
pr_cont(" %d(%ld,%ld %c)",
|
||||
cpu, c0, c1,
|
||||
"C."[rcu_segcblist_empty(&sdp->srcu_cblist)]);
|
||||
s0 += c0;
|
||||
s1 += c1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@
|
|||
#include <linux/smpboot.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/sched/isolation.h>
|
||||
#include <linux/sched/clock.h>
|
||||
#include "../time/tick-internal.h"
|
||||
|
||||
#include "tree.h"
|
||||
|
|
@ -210,9 +211,9 @@ static long rcu_get_n_cbs_cpu(int cpu)
|
|||
{
|
||||
struct rcu_data *rdp = per_cpu_ptr(&rcu_data, cpu);
|
||||
|
||||
if (rcu_segcblist_is_enabled(&rdp->cblist)) /* Online normal CPU? */
|
||||
if (rcu_segcblist_is_enabled(&rdp->cblist))
|
||||
return rcu_segcblist_n_cbs(&rdp->cblist);
|
||||
return rcu_get_n_cbs_nocb_cpu(rdp); /* Works for offline, too. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rcu_softirq_qs(void)
|
||||
|
|
@ -416,6 +417,12 @@ module_param(qlowmark, long, 0444);
|
|||
static ulong jiffies_till_first_fqs = ULONG_MAX;
|
||||
static ulong jiffies_till_next_fqs = ULONG_MAX;
|
||||
static bool rcu_kick_kthreads;
|
||||
static int rcu_divisor = 7;
|
||||
module_param(rcu_divisor, int, 0644);
|
||||
|
||||
/* Force an exit from rcu_do_batch() after 3 milliseconds. */
|
||||
static long rcu_resched_ns = 3 * NSEC_PER_MSEC;
|
||||
module_param(rcu_resched_ns, long, 0644);
|
||||
|
||||
/*
|
||||
* How long the grace period must be before we start recruiting
|
||||
|
|
@ -1251,6 +1258,7 @@ static bool rcu_accelerate_cbs(struct rcu_node *rnp, struct rcu_data *rdp)
|
|||
unsigned long gp_seq_req;
|
||||
bool ret = false;
|
||||
|
||||
rcu_lockdep_assert_cblist_protected(rdp);
|
||||
raw_lockdep_assert_held_rcu_node(rnp);
|
||||
|
||||
/* If no pending (not yet ready to invoke) callbacks, nothing to do. */
|
||||
|
|
@ -1292,7 +1300,7 @@ static void rcu_accelerate_cbs_unlocked(struct rcu_node *rnp,
|
|||
unsigned long c;
|
||||
bool needwake;
|
||||
|
||||
lockdep_assert_irqs_disabled();
|
||||
rcu_lockdep_assert_cblist_protected(rdp);
|
||||
c = rcu_seq_snap(&rcu_state.gp_seq);
|
||||
if (!rdp->gpwrap && ULONG_CMP_GE(rdp->gp_seq_needed, c)) {
|
||||
/* Old request still live, so mark recent callbacks. */
|
||||
|
|
@ -1318,6 +1326,7 @@ static void rcu_accelerate_cbs_unlocked(struct rcu_node *rnp,
|
|||
*/
|
||||
static bool rcu_advance_cbs(struct rcu_node *rnp, struct rcu_data *rdp)
|
||||
{
|
||||
rcu_lockdep_assert_cblist_protected(rdp);
|
||||
raw_lockdep_assert_held_rcu_node(rnp);
|
||||
|
||||
/* If no pending (not yet ready to invoke) callbacks, nothing to do. */
|
||||
|
|
@ -1334,6 +1343,21 @@ static bool rcu_advance_cbs(struct rcu_node *rnp, struct rcu_data *rdp)
|
|||
return rcu_accelerate_cbs(rnp, rdp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Move and classify callbacks, but only if doing so won't require
|
||||
* that the RCU grace-period kthread be awakened.
|
||||
*/
|
||||
static void __maybe_unused rcu_advance_cbs_nowake(struct rcu_node *rnp,
|
||||
struct rcu_data *rdp)
|
||||
{
|
||||
rcu_lockdep_assert_cblist_protected(rdp);
|
||||
if (!rcu_seq_state(rcu_seq_current(&rnp->gp_seq)) ||
|
||||
!raw_spin_trylock_rcu_node(rnp))
|
||||
return;
|
||||
WARN_ON_ONCE(rcu_advance_cbs(rnp, rdp));
|
||||
raw_spin_unlock_rcu_node(rnp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Update CPU-local rcu_data state to record the beginnings and ends of
|
||||
* grace periods. The caller must hold the ->lock of the leaf rcu_node
|
||||
|
|
@ -1342,8 +1366,10 @@ static bool rcu_advance_cbs(struct rcu_node *rnp, struct rcu_data *rdp)
|
|||
*/
|
||||
static bool __note_gp_changes(struct rcu_node *rnp, struct rcu_data *rdp)
|
||||
{
|
||||
bool ret;
|
||||
bool ret = false;
|
||||
bool need_gp;
|
||||
const bool offloaded = IS_ENABLED(CONFIG_RCU_NOCB_CPU) &&
|
||||
rcu_segcblist_is_offloaded(&rdp->cblist);
|
||||
|
||||
raw_lockdep_assert_held_rcu_node(rnp);
|
||||
|
||||
|
|
@ -1353,10 +1379,12 @@ static bool __note_gp_changes(struct rcu_node *rnp, struct rcu_data *rdp)
|
|||
/* Handle the ends of any preceding grace periods first. */
|
||||
if (rcu_seq_completed_gp(rdp->gp_seq, rnp->gp_seq) ||
|
||||
unlikely(READ_ONCE(rdp->gpwrap))) {
|
||||
ret = rcu_advance_cbs(rnp, rdp); /* Advance callbacks. */
|
||||
if (!offloaded)
|
||||
ret = rcu_advance_cbs(rnp, rdp); /* Advance CBs. */
|
||||
trace_rcu_grace_period(rcu_state.name, rdp->gp_seq, TPS("cpuend"));
|
||||
} else {
|
||||
ret = rcu_accelerate_cbs(rnp, rdp); /* Recent callbacks. */
|
||||
if (!offloaded)
|
||||
ret = rcu_accelerate_cbs(rnp, rdp); /* Recent CBs. */
|
||||
}
|
||||
|
||||
/* Now handle the beginnings of any new-to-this-CPU grace periods. */
|
||||
|
|
@ -1657,6 +1685,7 @@ static void rcu_gp_cleanup(void)
|
|||
unsigned long gp_duration;
|
||||
bool needgp = false;
|
||||
unsigned long new_gp_seq;
|
||||
bool offloaded;
|
||||
struct rcu_data *rdp;
|
||||
struct rcu_node *rnp = rcu_get_root();
|
||||
struct swait_queue_head *sq;
|
||||
|
|
@ -1722,7 +1751,9 @@ static void rcu_gp_cleanup(void)
|
|||
needgp = true;
|
||||
}
|
||||
/* Advance CBs to reduce false positives below. */
|
||||
if (!rcu_accelerate_cbs(rnp, rdp) && needgp) {
|
||||
offloaded = IS_ENABLED(CONFIG_RCU_NOCB_CPU) &&
|
||||
rcu_segcblist_is_offloaded(&rdp->cblist);
|
||||
if ((offloaded || !rcu_accelerate_cbs(rnp, rdp)) && needgp) {
|
||||
WRITE_ONCE(rcu_state.gp_flags, RCU_GP_FLAG_INIT);
|
||||
rcu_state.gp_req_activity = jiffies;
|
||||
trace_rcu_grace_period(rcu_state.name,
|
||||
|
|
@ -1881,7 +1912,7 @@ rcu_report_unblock_qs_rnp(struct rcu_node *rnp, unsigned long flags)
|
|||
struct rcu_node *rnp_p;
|
||||
|
||||
raw_lockdep_assert_held_rcu_node(rnp);
|
||||
if (WARN_ON_ONCE(!IS_ENABLED(CONFIG_PREEMPT)) ||
|
||||
if (WARN_ON_ONCE(!IS_ENABLED(CONFIG_PREEMPTION)) ||
|
||||
WARN_ON_ONCE(rcu_preempt_blocked_readers_cgp(rnp)) ||
|
||||
rnp->qsmask != 0) {
|
||||
raw_spin_unlock_irqrestore_rcu_node(rnp, flags);
|
||||
|
|
@ -1916,7 +1947,9 @@ rcu_report_qs_rdp(int cpu, struct rcu_data *rdp)
|
|||
{
|
||||
unsigned long flags;
|
||||
unsigned long mask;
|
||||
bool needwake;
|
||||
bool needwake = false;
|
||||
const bool offloaded = IS_ENABLED(CONFIG_RCU_NOCB_CPU) &&
|
||||
rcu_segcblist_is_offloaded(&rdp->cblist);
|
||||
struct rcu_node *rnp;
|
||||
|
||||
rnp = rdp->mynode;
|
||||
|
|
@ -1943,7 +1976,8 @@ rcu_report_qs_rdp(int cpu, struct rcu_data *rdp)
|
|||
* This GP can't end until cpu checks in, so all of our
|
||||
* callbacks can be processed during the next GP.
|
||||
*/
|
||||
needwake = rcu_accelerate_cbs(rnp, rdp);
|
||||
if (!offloaded)
|
||||
needwake = rcu_accelerate_cbs(rnp, rdp);
|
||||
|
||||
rcu_report_qs_rnp(mask, rnp, rnp->gp_seq, flags);
|
||||
/* ^^^ Released rnp->lock */
|
||||
|
|
@ -2077,9 +2111,12 @@ int rcutree_dead_cpu(unsigned int cpu)
|
|||
static void rcu_do_batch(struct rcu_data *rdp)
|
||||
{
|
||||
unsigned long flags;
|
||||
const bool offloaded = IS_ENABLED(CONFIG_RCU_NOCB_CPU) &&
|
||||
rcu_segcblist_is_offloaded(&rdp->cblist);
|
||||
struct rcu_head *rhp;
|
||||
struct rcu_cblist rcl = RCU_CBLIST_INITIALIZER(rcl);
|
||||
long bl, count;
|
||||
long pending, tlimit = 0;
|
||||
|
||||
/* If no callbacks are ready, just return. */
|
||||
if (!rcu_segcblist_ready_cbs(&rdp->cblist)) {
|
||||
|
|
@ -2099,13 +2136,19 @@ static void rcu_do_batch(struct rcu_data *rdp)
|
|||
* callback counts, as rcu_barrier() needs to be conservative.
|
||||
*/
|
||||
local_irq_save(flags);
|
||||
rcu_nocb_lock(rdp);
|
||||
WARN_ON_ONCE(cpu_is_offline(smp_processor_id()));
|
||||
bl = rdp->blimit;
|
||||
pending = rcu_segcblist_n_cbs(&rdp->cblist);
|
||||
bl = max(rdp->blimit, pending >> rcu_divisor);
|
||||
if (unlikely(bl > 100))
|
||||
tlimit = local_clock() + rcu_resched_ns;
|
||||
trace_rcu_batch_start(rcu_state.name,
|
||||
rcu_segcblist_n_lazy_cbs(&rdp->cblist),
|
||||
rcu_segcblist_n_cbs(&rdp->cblist), bl);
|
||||
rcu_segcblist_extract_done_cbs(&rdp->cblist, &rcl);
|
||||
local_irq_restore(flags);
|
||||
if (offloaded)
|
||||
rdp->qlen_last_fqs_check = rcu_segcblist_n_cbs(&rdp->cblist);
|
||||
rcu_nocb_unlock_irqrestore(rdp, flags);
|
||||
|
||||
/* Invoke callbacks. */
|
||||
rhp = rcu_cblist_dequeue(&rcl);
|
||||
|
|
@ -2117,13 +2160,29 @@ static void rcu_do_batch(struct rcu_data *rdp)
|
|||
* Stop only if limit reached and CPU has something to do.
|
||||
* Note: The rcl structure counts down from zero.
|
||||
*/
|
||||
if (-rcl.len >= bl &&
|
||||
if (-rcl.len >= bl && !offloaded &&
|
||||
(need_resched() ||
|
||||
(!is_idle_task(current) && !rcu_is_callbacks_kthread())))
|
||||
break;
|
||||
if (unlikely(tlimit)) {
|
||||
/* only call local_clock() every 32 callbacks */
|
||||
if (likely((-rcl.len & 31) || local_clock() < tlimit))
|
||||
continue;
|
||||
/* Exceeded the time limit, so leave. */
|
||||
break;
|
||||
}
|
||||
if (offloaded) {
|
||||
WARN_ON_ONCE(in_serving_softirq());
|
||||
local_bh_enable();
|
||||
lockdep_assert_irqs_enabled();
|
||||
cond_resched_tasks_rcu_qs();
|
||||
lockdep_assert_irqs_enabled();
|
||||
local_bh_disable();
|
||||
}
|
||||
}
|
||||
|
||||
local_irq_save(flags);
|
||||
rcu_nocb_lock(rdp);
|
||||
count = -rcl.len;
|
||||
trace_rcu_batch_end(rcu_state.name, count, !!rcl.head, need_resched(),
|
||||
is_idle_task(current), rcu_is_callbacks_kthread());
|
||||
|
|
@ -2149,12 +2208,14 @@ static void rcu_do_batch(struct rcu_data *rdp)
|
|||
* The following usually indicates a double call_rcu(). To track
|
||||
* this down, try building with CONFIG_DEBUG_OBJECTS_RCU_HEAD=y.
|
||||
*/
|
||||
WARN_ON_ONCE(rcu_segcblist_empty(&rdp->cblist) != (count == 0));
|
||||
WARN_ON_ONCE(count == 0 && !rcu_segcblist_empty(&rdp->cblist));
|
||||
WARN_ON_ONCE(!IS_ENABLED(CONFIG_RCU_NOCB_CPU) &&
|
||||
count != 0 && rcu_segcblist_empty(&rdp->cblist));
|
||||
|
||||
local_irq_restore(flags);
|
||||
rcu_nocb_unlock_irqrestore(rdp, flags);
|
||||
|
||||
/* Re-invoke RCU core processing if there are callbacks remaining. */
|
||||
if (rcu_segcblist_ready_cbs(&rdp->cblist))
|
||||
if (!offloaded && rcu_segcblist_ready_cbs(&rdp->cblist))
|
||||
invoke_rcu_core();
|
||||
}
|
||||
|
||||
|
|
@ -2205,7 +2266,7 @@ static void force_qs_rnp(int (*f)(struct rcu_data *rdp))
|
|||
mask = 0;
|
||||
raw_spin_lock_irqsave_rcu_node(rnp, flags);
|
||||
if (rnp->qsmask == 0) {
|
||||
if (!IS_ENABLED(CONFIG_PREEMPT) ||
|
||||
if (!IS_ENABLED(CONFIG_PREEMPTION) ||
|
||||
rcu_preempt_blocked_readers_cgp(rnp)) {
|
||||
/*
|
||||
* No point in scanning bits because they
|
||||
|
|
@ -2280,6 +2341,8 @@ static __latent_entropy void rcu_core(void)
|
|||
unsigned long flags;
|
||||
struct rcu_data *rdp = raw_cpu_ptr(&rcu_data);
|
||||
struct rcu_node *rnp = rdp->mynode;
|
||||
const bool offloaded = IS_ENABLED(CONFIG_RCU_NOCB_CPU) &&
|
||||
rcu_segcblist_is_offloaded(&rdp->cblist);
|
||||
|
||||
if (cpu_is_offline(smp_processor_id()))
|
||||
return;
|
||||
|
|
@ -2299,7 +2362,7 @@ static __latent_entropy void rcu_core(void)
|
|||
|
||||
/* No grace period and unregistered callbacks? */
|
||||
if (!rcu_gp_in_progress() &&
|
||||
rcu_segcblist_is_enabled(&rdp->cblist)) {
|
||||
rcu_segcblist_is_enabled(&rdp->cblist) && !offloaded) {
|
||||
local_irq_save(flags);
|
||||
if (!rcu_segcblist_restempty(&rdp->cblist, RCU_NEXT_READY_TAIL))
|
||||
rcu_accelerate_cbs_unlocked(rnp, rdp);
|
||||
|
|
@ -2309,7 +2372,7 @@ static __latent_entropy void rcu_core(void)
|
|||
rcu_check_gp_start_stall(rnp, rdp, rcu_jiffies_till_stall_check());
|
||||
|
||||
/* If there are callbacks ready, invoke them. */
|
||||
if (rcu_segcblist_ready_cbs(&rdp->cblist) &&
|
||||
if (!offloaded && rcu_segcblist_ready_cbs(&rdp->cblist) &&
|
||||
likely(READ_ONCE(rcu_scheduler_fully_active)))
|
||||
rcu_do_batch(rdp);
|
||||
|
||||
|
|
@ -2489,10 +2552,11 @@ static void rcu_leak_callback(struct rcu_head *rhp)
|
|||
* is expected to specify a CPU.
|
||||
*/
|
||||
static void
|
||||
__call_rcu(struct rcu_head *head, rcu_callback_t func, int cpu, bool lazy)
|
||||
__call_rcu(struct rcu_head *head, rcu_callback_t func, bool lazy)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct rcu_data *rdp;
|
||||
bool was_alldone;
|
||||
|
||||
/* Misaligned rcu_head! */
|
||||
WARN_ON_ONCE((unsigned long)head & (sizeof(void *) - 1));
|
||||
|
|
@ -2514,28 +2578,18 @@ __call_rcu(struct rcu_head *head, rcu_callback_t func, int cpu, bool lazy)
|
|||
rdp = this_cpu_ptr(&rcu_data);
|
||||
|
||||
/* Add the callback to our list. */
|
||||
if (unlikely(!rcu_segcblist_is_enabled(&rdp->cblist)) || cpu != -1) {
|
||||
int offline;
|
||||
|
||||
if (cpu != -1)
|
||||
rdp = per_cpu_ptr(&rcu_data, cpu);
|
||||
if (likely(rdp->mynode)) {
|
||||
/* Post-boot, so this should be for a no-CBs CPU. */
|
||||
offline = !__call_rcu_nocb(rdp, head, lazy, flags);
|
||||
WARN_ON_ONCE(offline);
|
||||
/* Offline CPU, _call_rcu() illegal, leak callback. */
|
||||
local_irq_restore(flags);
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* Very early boot, before rcu_init(). Initialize if needed
|
||||
* and then drop through to queue the callback.
|
||||
*/
|
||||
WARN_ON_ONCE(cpu != -1);
|
||||
if (unlikely(!rcu_segcblist_is_enabled(&rdp->cblist))) {
|
||||
// This can trigger due to call_rcu() from offline CPU:
|
||||
WARN_ON_ONCE(rcu_scheduler_active != RCU_SCHEDULER_INACTIVE);
|
||||
WARN_ON_ONCE(!rcu_is_watching());
|
||||
// Very early boot, before rcu_init(). Initialize if needed
|
||||
// and then drop through to queue the callback.
|
||||
if (rcu_segcblist_empty(&rdp->cblist))
|
||||
rcu_segcblist_init(&rdp->cblist);
|
||||
}
|
||||
if (rcu_nocb_try_bypass(rdp, head, &was_alldone, flags))
|
||||
return; // Enqueued onto ->nocb_bypass, so just leave.
|
||||
/* If we get here, rcu_nocb_try_bypass() acquired ->nocb_lock. */
|
||||
rcu_segcblist_enqueue(&rdp->cblist, head, lazy);
|
||||
if (__is_kfree_rcu_offset((unsigned long)func))
|
||||
trace_rcu_kfree_callback(rcu_state.name, head,
|
||||
|
|
@ -2548,8 +2602,13 @@ __call_rcu(struct rcu_head *head, rcu_callback_t func, int cpu, bool lazy)
|
|||
rcu_segcblist_n_cbs(&rdp->cblist));
|
||||
|
||||
/* Go handle any RCU core processing required. */
|
||||
__call_rcu_core(rdp, head, flags);
|
||||
local_irq_restore(flags);
|
||||
if (IS_ENABLED(CONFIG_RCU_NOCB_CPU) &&
|
||||
unlikely(rcu_segcblist_is_offloaded(&rdp->cblist))) {
|
||||
__call_rcu_nocb_wake(rdp, was_alldone, flags); /* unlocks */
|
||||
} else {
|
||||
__call_rcu_core(rdp, head, flags);
|
||||
local_irq_restore(flags);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -2589,7 +2648,7 @@ __call_rcu(struct rcu_head *head, rcu_callback_t func, int cpu, bool lazy)
|
|||
*/
|
||||
void call_rcu(struct rcu_head *head, rcu_callback_t func)
|
||||
{
|
||||
__call_rcu(head, func, -1, 0);
|
||||
__call_rcu(head, func, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(call_rcu);
|
||||
|
||||
|
|
@ -2602,7 +2661,7 @@ EXPORT_SYMBOL_GPL(call_rcu);
|
|||
*/
|
||||
void kfree_call_rcu(struct rcu_head *head, rcu_callback_t func)
|
||||
{
|
||||
__call_rcu(head, func, -1, 1);
|
||||
__call_rcu(head, func, 1);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kfree_call_rcu);
|
||||
|
||||
|
|
@ -2622,7 +2681,7 @@ static int rcu_blocking_is_gp(void)
|
|||
{
|
||||
int ret;
|
||||
|
||||
if (IS_ENABLED(CONFIG_PREEMPT))
|
||||
if (IS_ENABLED(CONFIG_PREEMPTION))
|
||||
return rcu_scheduler_active == RCU_SCHEDULER_INACTIVE;
|
||||
might_sleep(); /* Check for RCU read-side critical section. */
|
||||
preempt_disable();
|
||||
|
|
@ -2735,6 +2794,10 @@ static int rcu_pending(void)
|
|||
/* Check for CPU stalls, if enabled. */
|
||||
check_cpu_stall(rdp);
|
||||
|
||||
/* Does this CPU need a deferred NOCB wakeup? */
|
||||
if (rcu_nocb_need_deferred_wakeup(rdp))
|
||||
return 1;
|
||||
|
||||
/* Is this CPU a NO_HZ_FULL CPU that should ignore RCU? */
|
||||
if (rcu_nohz_full_cpu())
|
||||
return 0;
|
||||
|
|
@ -2750,6 +2813,8 @@ static int rcu_pending(void)
|
|||
/* Has RCU gone idle with this CPU needing another grace period? */
|
||||
if (!rcu_gp_in_progress() &&
|
||||
rcu_segcblist_is_enabled(&rdp->cblist) &&
|
||||
(!IS_ENABLED(CONFIG_RCU_NOCB_CPU) ||
|
||||
!rcu_segcblist_is_offloaded(&rdp->cblist)) &&
|
||||
!rcu_segcblist_restempty(&rdp->cblist, RCU_NEXT_READY_TAIL))
|
||||
return 1;
|
||||
|
||||
|
|
@ -2758,10 +2823,6 @@ static int rcu_pending(void)
|
|||
unlikely(READ_ONCE(rdp->gpwrap))) /* outside lock */
|
||||
return 1;
|
||||
|
||||
/* Does this CPU need a deferred NOCB wakeup? */
|
||||
if (rcu_nocb_need_deferred_wakeup(rdp))
|
||||
return 1;
|
||||
|
||||
/* nothing to do */
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -2801,6 +2862,8 @@ static void rcu_barrier_func(void *unused)
|
|||
rcu_barrier_trace(TPS("IRQ"), -1, rcu_state.barrier_sequence);
|
||||
rdp->barrier_head.func = rcu_barrier_callback;
|
||||
debug_rcu_head_queue(&rdp->barrier_head);
|
||||
rcu_nocb_lock(rdp);
|
||||
WARN_ON_ONCE(!rcu_nocb_flush_bypass(rdp, NULL, jiffies));
|
||||
if (rcu_segcblist_entrain(&rdp->cblist, &rdp->barrier_head, 0)) {
|
||||
atomic_inc(&rcu_state.barrier_cpu_count);
|
||||
} else {
|
||||
|
|
@ -2808,6 +2871,7 @@ static void rcu_barrier_func(void *unused)
|
|||
rcu_barrier_trace(TPS("IRQNQ"), -1,
|
||||
rcu_state.barrier_sequence);
|
||||
}
|
||||
rcu_nocb_unlock(rdp);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -2858,22 +2922,11 @@ void rcu_barrier(void)
|
|||
* corresponding CPU's preceding callbacks have been invoked.
|
||||
*/
|
||||
for_each_possible_cpu(cpu) {
|
||||
if (!cpu_online(cpu) && !rcu_is_nocb_cpu(cpu))
|
||||
continue;
|
||||
rdp = per_cpu_ptr(&rcu_data, cpu);
|
||||
if (rcu_is_nocb_cpu(cpu)) {
|
||||
if (!rcu_nocb_cpu_needs_barrier(cpu)) {
|
||||
rcu_barrier_trace(TPS("OfflineNoCB"), cpu,
|
||||
rcu_state.barrier_sequence);
|
||||
} else {
|
||||
rcu_barrier_trace(TPS("OnlineNoCB"), cpu,
|
||||
rcu_state.barrier_sequence);
|
||||
smp_mb__before_atomic();
|
||||
atomic_inc(&rcu_state.barrier_cpu_count);
|
||||
__call_rcu(&rdp->barrier_head,
|
||||
rcu_barrier_callback, cpu, 0);
|
||||
}
|
||||
} else if (rcu_segcblist_n_cbs(&rdp->cblist)) {
|
||||
if (!cpu_online(cpu) &&
|
||||
!rcu_segcblist_is_offloaded(&rdp->cblist))
|
||||
continue;
|
||||
if (rcu_segcblist_n_cbs(&rdp->cblist)) {
|
||||
rcu_barrier_trace(TPS("OnlineQ"), cpu,
|
||||
rcu_state.barrier_sequence);
|
||||
smp_call_function_single(cpu, rcu_barrier_func, NULL, 1);
|
||||
|
|
@ -2958,7 +3011,8 @@ rcu_boot_init_percpu_data(int cpu)
|
|||
* Initializes a CPU's per-CPU RCU data. Note that only one online or
|
||||
* offline event can be happening at a given time. Note also that we can
|
||||
* accept some slop in the rsp->gp_seq access due to the fact that this
|
||||
* CPU cannot possibly have any RCU callbacks in flight yet.
|
||||
* CPU cannot possibly have any non-offloaded RCU callbacks in flight yet.
|
||||
* And any offloaded callbacks are being numbered elsewhere.
|
||||
*/
|
||||
int rcutree_prepare_cpu(unsigned int cpu)
|
||||
{
|
||||
|
|
@ -2972,7 +3026,7 @@ int rcutree_prepare_cpu(unsigned int cpu)
|
|||
rdp->n_force_qs_snap = rcu_state.n_force_qs;
|
||||
rdp->blimit = blimit;
|
||||
if (rcu_segcblist_empty(&rdp->cblist) && /* No early-boot CBs? */
|
||||
!init_nocb_callback_list(rdp))
|
||||
!rcu_segcblist_is_offloaded(&rdp->cblist))
|
||||
rcu_segcblist_init(&rdp->cblist); /* Re-enable callbacks. */
|
||||
rdp->dynticks_nesting = 1; /* CPU not up, no tearing. */
|
||||
rcu_dynticks_eqs_online();
|
||||
|
|
@ -3151,29 +3205,38 @@ void rcutree_migrate_callbacks(int cpu)
|
|||
{
|
||||
unsigned long flags;
|
||||
struct rcu_data *my_rdp;
|
||||
struct rcu_node *my_rnp;
|
||||
struct rcu_data *rdp = per_cpu_ptr(&rcu_data, cpu);
|
||||
struct rcu_node *rnp_root = rcu_get_root();
|
||||
bool needwake;
|
||||
|
||||
if (rcu_is_nocb_cpu(cpu) || rcu_segcblist_empty(&rdp->cblist))
|
||||
if (rcu_segcblist_is_offloaded(&rdp->cblist) ||
|
||||
rcu_segcblist_empty(&rdp->cblist))
|
||||
return; /* No callbacks to migrate. */
|
||||
|
||||
local_irq_save(flags);
|
||||
my_rdp = this_cpu_ptr(&rcu_data);
|
||||
if (rcu_nocb_adopt_orphan_cbs(my_rdp, rdp, flags)) {
|
||||
local_irq_restore(flags);
|
||||
return;
|
||||
}
|
||||
raw_spin_lock_rcu_node(rnp_root); /* irqs already disabled. */
|
||||
my_rnp = my_rdp->mynode;
|
||||
rcu_nocb_lock(my_rdp); /* irqs already disabled. */
|
||||
WARN_ON_ONCE(!rcu_nocb_flush_bypass(my_rdp, NULL, jiffies));
|
||||
raw_spin_lock_rcu_node(my_rnp); /* irqs already disabled. */
|
||||
/* Leverage recent GPs and set GP for new callbacks. */
|
||||
needwake = rcu_advance_cbs(rnp_root, rdp) ||
|
||||
rcu_advance_cbs(rnp_root, my_rdp);
|
||||
needwake = rcu_advance_cbs(my_rnp, rdp) ||
|
||||
rcu_advance_cbs(my_rnp, my_rdp);
|
||||
rcu_segcblist_merge(&my_rdp->cblist, &rdp->cblist);
|
||||
needwake = needwake || rcu_advance_cbs(my_rnp, my_rdp);
|
||||
rcu_segcblist_disable(&rdp->cblist);
|
||||
WARN_ON_ONCE(rcu_segcblist_empty(&my_rdp->cblist) !=
|
||||
!rcu_segcblist_n_cbs(&my_rdp->cblist));
|
||||
raw_spin_unlock_irqrestore_rcu_node(rnp_root, flags);
|
||||
if (rcu_segcblist_is_offloaded(&my_rdp->cblist)) {
|
||||
raw_spin_unlock_rcu_node(my_rnp); /* irqs remain disabled. */
|
||||
__call_rcu_nocb_wake(my_rdp, true, flags);
|
||||
} else {
|
||||
rcu_nocb_unlock(my_rdp); /* irqs remain disabled. */
|
||||
raw_spin_unlock_irqrestore_rcu_node(my_rnp, flags);
|
||||
}
|
||||
if (needwake)
|
||||
rcu_gp_kthread_wake();
|
||||
lockdep_assert_irqs_enabled();
|
||||
WARN_ONCE(rcu_segcblist_n_cbs(&rdp->cblist) != 0 ||
|
||||
!rcu_segcblist_empty(&rdp->cblist),
|
||||
"rcu_cleanup_dead_cpu: Callbacks on offline CPU %d: qlen=%lu, 1stCB=%p\n",
|
||||
|
|
@ -3234,13 +3297,13 @@ static int __init rcu_spawn_gp_kthread(void)
|
|||
t = kthread_create(rcu_gp_kthread, NULL, "%s", rcu_state.name);
|
||||
if (WARN_ONCE(IS_ERR(t), "%s: Could not start grace-period kthread, OOM is now expected behavior\n", __func__))
|
||||
return 0;
|
||||
rnp = rcu_get_root();
|
||||
raw_spin_lock_irqsave_rcu_node(rnp, flags);
|
||||
rcu_state.gp_kthread = t;
|
||||
if (kthread_prio) {
|
||||
sp.sched_priority = kthread_prio;
|
||||
sched_setscheduler_nocheck(t, SCHED_FIFO, &sp);
|
||||
}
|
||||
rnp = rcu_get_root();
|
||||
raw_spin_lock_irqsave_rcu_node(rnp, flags);
|
||||
rcu_state.gp_kthread = t;
|
||||
raw_spin_unlock_irqrestore_rcu_node(rnp, flags);
|
||||
wake_up_process(t);
|
||||
rcu_spawn_nocb_kthreads();
|
||||
|
|
|
|||
|
|
@ -194,29 +194,38 @@ struct rcu_data {
|
|||
|
||||
/* 5) Callback offloading. */
|
||||
#ifdef CONFIG_RCU_NOCB_CPU
|
||||
struct rcu_head *nocb_head; /* CBs waiting for kthread. */
|
||||
struct rcu_head **nocb_tail;
|
||||
atomic_long_t nocb_q_count; /* # CBs waiting for nocb */
|
||||
atomic_long_t nocb_q_count_lazy; /* invocation (all stages). */
|
||||
struct rcu_head *nocb_follower_head; /* CBs ready to invoke. */
|
||||
struct rcu_head **nocb_follower_tail;
|
||||
struct swait_queue_head nocb_wq; /* For nocb kthreads to sleep on. */
|
||||
struct task_struct *nocb_kthread;
|
||||
struct swait_queue_head nocb_cb_wq; /* For nocb kthreads to sleep on. */
|
||||
struct task_struct *nocb_gp_kthread;
|
||||
raw_spinlock_t nocb_lock; /* Guard following pair of fields. */
|
||||
atomic_t nocb_lock_contended; /* Contention experienced. */
|
||||
int nocb_defer_wakeup; /* Defer wakeup of nocb_kthread. */
|
||||
struct timer_list nocb_timer; /* Enforce finite deferral. */
|
||||
unsigned long nocb_gp_adv_time; /* Last call_rcu() CB adv (jiffies). */
|
||||
|
||||
/* The following fields are used by the leader, hence own cacheline. */
|
||||
struct rcu_head *nocb_gp_head ____cacheline_internodealigned_in_smp;
|
||||
/* CBs waiting for GP. */
|
||||
struct rcu_head **nocb_gp_tail;
|
||||
bool nocb_leader_sleep; /* Is the nocb leader thread asleep? */
|
||||
struct rcu_data *nocb_next_follower;
|
||||
/* Next follower in wakeup chain. */
|
||||
/* The following fields are used by call_rcu, hence own cacheline. */
|
||||
raw_spinlock_t nocb_bypass_lock ____cacheline_internodealigned_in_smp;
|
||||
struct rcu_cblist nocb_bypass; /* Lock-contention-bypass CB list. */
|
||||
unsigned long nocb_bypass_first; /* Time (jiffies) of first enqueue. */
|
||||
unsigned long nocb_nobypass_last; /* Last ->cblist enqueue (jiffies). */
|
||||
int nocb_nobypass_count; /* # ->cblist enqueues at ^^^ time. */
|
||||
|
||||
/* The following fields are used by the follower, hence new cachline. */
|
||||
struct rcu_data *nocb_leader ____cacheline_internodealigned_in_smp;
|
||||
/* Leader CPU takes GP-end wakeups. */
|
||||
/* The following fields are used by GP kthread, hence own cacheline. */
|
||||
raw_spinlock_t nocb_gp_lock ____cacheline_internodealigned_in_smp;
|
||||
struct timer_list nocb_bypass_timer; /* Force nocb_bypass flush. */
|
||||
u8 nocb_gp_sleep; /* Is the nocb GP thread asleep? */
|
||||
u8 nocb_gp_bypass; /* Found a bypass on last scan? */
|
||||
u8 nocb_gp_gp; /* GP to wait for on last scan? */
|
||||
unsigned long nocb_gp_seq; /* If so, ->gp_seq to wait for. */
|
||||
unsigned long nocb_gp_loops; /* # passes through wait code. */
|
||||
struct swait_queue_head nocb_gp_wq; /* For nocb kthreads to sleep on. */
|
||||
bool nocb_cb_sleep; /* Is the nocb CB thread asleep? */
|
||||
struct task_struct *nocb_cb_kthread;
|
||||
struct rcu_data *nocb_next_cb_rdp;
|
||||
/* Next rcu_data in wakeup chain. */
|
||||
|
||||
/* The following fields are used by CB kthread, hence new cacheline. */
|
||||
struct rcu_data *nocb_gp_rdp ____cacheline_internodealigned_in_smp;
|
||||
/* GP rdp takes GP-end wakeups. */
|
||||
#endif /* #ifdef CONFIG_RCU_NOCB_CPU */
|
||||
|
||||
/* 6) RCU priority boosting. */
|
||||
|
|
@ -419,25 +428,39 @@ static bool rcu_preempt_has_tasks(struct rcu_node *rnp);
|
|||
static bool rcu_preempt_need_deferred_qs(struct task_struct *t);
|
||||
static void rcu_preempt_deferred_qs(struct task_struct *t);
|
||||
static void zero_cpu_stall_ticks(struct rcu_data *rdp);
|
||||
static bool rcu_nocb_cpu_needs_barrier(int cpu);
|
||||
static struct swait_queue_head *rcu_nocb_gp_get(struct rcu_node *rnp);
|
||||
static void rcu_nocb_gp_cleanup(struct swait_queue_head *sq);
|
||||
static void rcu_init_one_nocb(struct rcu_node *rnp);
|
||||
static bool __call_rcu_nocb(struct rcu_data *rdp, struct rcu_head *rhp,
|
||||
bool lazy, unsigned long flags);
|
||||
static bool rcu_nocb_adopt_orphan_cbs(struct rcu_data *my_rdp,
|
||||
struct rcu_data *rdp,
|
||||
unsigned long flags);
|
||||
static bool rcu_nocb_flush_bypass(struct rcu_data *rdp, struct rcu_head *rhp,
|
||||
unsigned long j);
|
||||
static bool rcu_nocb_try_bypass(struct rcu_data *rdp, struct rcu_head *rhp,
|
||||
bool *was_alldone, unsigned long flags);
|
||||
static void __call_rcu_nocb_wake(struct rcu_data *rdp, bool was_empty,
|
||||
unsigned long flags);
|
||||
static int rcu_nocb_need_deferred_wakeup(struct rcu_data *rdp);
|
||||
static void do_nocb_deferred_wakeup(struct rcu_data *rdp);
|
||||
static void rcu_boot_init_nocb_percpu_data(struct rcu_data *rdp);
|
||||
static void rcu_spawn_cpu_nocb_kthread(int cpu);
|
||||
static void __init rcu_spawn_nocb_kthreads(void);
|
||||
static void show_rcu_nocb_state(struct rcu_data *rdp);
|
||||
static void rcu_nocb_lock(struct rcu_data *rdp);
|
||||
static void rcu_nocb_unlock(struct rcu_data *rdp);
|
||||
static void rcu_nocb_unlock_irqrestore(struct rcu_data *rdp,
|
||||
unsigned long flags);
|
||||
static void rcu_lockdep_assert_cblist_protected(struct rcu_data *rdp);
|
||||
#ifdef CONFIG_RCU_NOCB_CPU
|
||||
static void __init rcu_organize_nocb_kthreads(void);
|
||||
#endif /* #ifdef CONFIG_RCU_NOCB_CPU */
|
||||
static bool init_nocb_callback_list(struct rcu_data *rdp);
|
||||
static unsigned long rcu_get_n_cbs_nocb_cpu(struct rcu_data *rdp);
|
||||
#define rcu_nocb_lock_irqsave(rdp, flags) \
|
||||
do { \
|
||||
if (!rcu_segcblist_is_offloaded(&(rdp)->cblist)) \
|
||||
local_irq_save(flags); \
|
||||
else \
|
||||
raw_spin_lock_irqsave(&(rdp)->nocb_lock, (flags)); \
|
||||
} while (0)
|
||||
#else /* #ifdef CONFIG_RCU_NOCB_CPU */
|
||||
#define rcu_nocb_lock_irqsave(rdp, flags) local_irq_save(flags)
|
||||
#endif /* #else #ifdef CONFIG_RCU_NOCB_CPU */
|
||||
|
||||
static void rcu_bind_gp_kthread(void);
|
||||
static bool rcu_nohz_full_cpu(void);
|
||||
static void rcu_dynticks_task_enter(void);
|
||||
|
|
|
|||
|
|
@ -781,7 +781,7 @@ static int rcu_print_task_exp_stall(struct rcu_node *rnp)
|
|||
* other hand, if the CPU is not in an RCU read-side critical section,
|
||||
* the IPI handler reports the quiescent state immediately.
|
||||
*
|
||||
* Although this is a greate improvement over previous expedited
|
||||
* Although this is a great improvement over previous expedited
|
||||
* implementations, it is still unfriendly to real-time workloads, so is
|
||||
* thus not recommended for any sort of common-case code. In fact, if
|
||||
* you are using synchronize_rcu_expedited() in a loop, please restructure
|
||||
|
|
@ -792,6 +792,7 @@ static int rcu_print_task_exp_stall(struct rcu_node *rnp)
|
|||
*/
|
||||
void synchronize_rcu_expedited(void)
|
||||
{
|
||||
bool boottime = (rcu_scheduler_active == RCU_SCHEDULER_INIT);
|
||||
struct rcu_exp_work rew;
|
||||
struct rcu_node *rnp;
|
||||
unsigned long s;
|
||||
|
|
@ -817,7 +818,7 @@ void synchronize_rcu_expedited(void)
|
|||
return; /* Someone else did our work for us. */
|
||||
|
||||
/* Ensure that load happens before action based on it. */
|
||||
if (unlikely(rcu_scheduler_active == RCU_SCHEDULER_INIT)) {
|
||||
if (unlikely(boottime)) {
|
||||
/* Direct call during scheduler init and early_initcalls(). */
|
||||
rcu_exp_sel_wait_wake(s);
|
||||
} else {
|
||||
|
|
@ -835,5 +836,8 @@ void synchronize_rcu_expedited(void)
|
|||
|
||||
/* Let the next expedited grace period start. */
|
||||
mutex_unlock(&rcu_state.exp_mutex);
|
||||
|
||||
if (likely(!boottime))
|
||||
destroy_work_on_stack(&rew.rew_work);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(synchronize_rcu_expedited);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -163,7 +163,7 @@ static void rcu_iw_handler(struct irq_work *iwp)
|
|||
//
|
||||
// Printing RCU CPU stall warnings
|
||||
|
||||
#ifdef CONFIG_PREEMPT
|
||||
#ifdef CONFIG_PREEMPTION
|
||||
|
||||
/*
|
||||
* Dump detailed information for all tasks blocking the current RCU
|
||||
|
|
@ -215,7 +215,7 @@ static int rcu_print_task_stall(struct rcu_node *rnp)
|
|||
return ndetected;
|
||||
}
|
||||
|
||||
#else /* #ifdef CONFIG_PREEMPT */
|
||||
#else /* #ifdef CONFIG_PREEMPTION */
|
||||
|
||||
/*
|
||||
* Because preemptible RCU does not exist, we never have to check for
|
||||
|
|
@ -233,7 +233,7 @@ static int rcu_print_task_stall(struct rcu_node *rnp)
|
|||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* #else #ifdef CONFIG_PREEMPT */
|
||||
#endif /* #else #ifdef CONFIG_PREEMPTION */
|
||||
|
||||
/*
|
||||
* Dump stacks of all tasks running on stalled CPUs. First try using
|
||||
|
|
@ -527,6 +527,8 @@ static void check_cpu_stall(struct rcu_data *rdp)
|
|||
|
||||
/* We haven't checked in, so go dump stack. */
|
||||
print_cpu_stall();
|
||||
if (rcu_cpu_stall_ftrace_dump)
|
||||
rcu_ftrace_dump(DUMP_ALL);
|
||||
|
||||
} else if (rcu_gp_in_progress() &&
|
||||
ULONG_CMP_GE(j, js + RCU_STALL_RAT_DELAY) &&
|
||||
|
|
@ -534,6 +536,8 @@ static void check_cpu_stall(struct rcu_data *rdp)
|
|||
|
||||
/* They had a few time units to dump stack, so complain. */
|
||||
print_other_cpu_stall(gs2);
|
||||
if (rcu_cpu_stall_ftrace_dump)
|
||||
rcu_ftrace_dump(DUMP_ALL);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -585,6 +589,11 @@ void show_rcu_gp_kthreads(void)
|
|||
cpu, (long)rdp->gp_seq_needed);
|
||||
}
|
||||
}
|
||||
for_each_possible_cpu(cpu) {
|
||||
rdp = per_cpu_ptr(&rcu_data, cpu);
|
||||
if (rcu_segcblist_is_offloaded(&rdp->cblist))
|
||||
show_rcu_nocb_state(rdp);
|
||||
}
|
||||
/* sched_show_task(rcu_state.gp_kthread); */
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(show_rcu_gp_kthreads);
|
||||
|
|
|
|||
|
|
@ -61,9 +61,15 @@ module_param(rcu_normal_after_boot, int, 0);
|
|||
|
||||
#ifdef CONFIG_DEBUG_LOCK_ALLOC
|
||||
/**
|
||||
* rcu_read_lock_sched_held() - might we be in RCU-sched read-side critical section?
|
||||
* rcu_read_lock_held_common() - might we be in RCU-sched read-side critical section?
|
||||
* @ret: Best guess answer if lockdep cannot be relied on
|
||||
*
|
||||
* If CONFIG_DEBUG_LOCK_ALLOC is selected, returns nonzero iff in an
|
||||
* Returns true if lockdep must be ignored, in which case *ret contains
|
||||
* the best guess described below. Otherwise returns false, in which
|
||||
* case *ret tells the caller nothing and the caller should instead
|
||||
* consult lockdep.
|
||||
*
|
||||
* If CONFIG_DEBUG_LOCK_ALLOC is selected, set *ret to nonzero iff in an
|
||||
* RCU-sched read-side critical section. In absence of
|
||||
* CONFIG_DEBUG_LOCK_ALLOC, this assumes we are in an RCU-sched read-side
|
||||
* critical section unless it can prove otherwise. Note that disabling
|
||||
|
|
@ -75,35 +81,45 @@ module_param(rcu_normal_after_boot, int, 0);
|
|||
* Check debug_lockdep_rcu_enabled() to prevent false positives during boot
|
||||
* and while lockdep is disabled.
|
||||
*
|
||||
* Note that if the CPU is in the idle loop from an RCU point of
|
||||
* view (ie: that we are in the section between rcu_idle_enter() and
|
||||
* rcu_idle_exit()) then rcu_read_lock_held() returns false even if the CPU
|
||||
* did an rcu_read_lock(). The reason for this is that RCU ignores CPUs
|
||||
* that are in such a section, considering these as in extended quiescent
|
||||
* state, so such a CPU is effectively never in an RCU read-side critical
|
||||
* section regardless of what RCU primitives it invokes. This state of
|
||||
* affairs is required --- we need to keep an RCU-free window in idle
|
||||
* where the CPU may possibly enter into low power mode. This way we can
|
||||
* notice an extended quiescent state to other CPUs that started a grace
|
||||
* period. Otherwise we would delay any grace period as long as we run in
|
||||
* the idle task.
|
||||
* Note that if the CPU is in the idle loop from an RCU point of view (ie:
|
||||
* that we are in the section between rcu_idle_enter() and rcu_idle_exit())
|
||||
* then rcu_read_lock_held() sets *ret to false even if the CPU did an
|
||||
* rcu_read_lock(). The reason for this is that RCU ignores CPUs that are
|
||||
* in such a section, considering these as in extended quiescent state,
|
||||
* so such a CPU is effectively never in an RCU read-side critical section
|
||||
* regardless of what RCU primitives it invokes. This state of affairs is
|
||||
* required --- we need to keep an RCU-free window in idle where the CPU may
|
||||
* possibly enter into low power mode. This way we can notice an extended
|
||||
* quiescent state to other CPUs that started a grace period. Otherwise
|
||||
* we would delay any grace period as long as we run in the idle task.
|
||||
*
|
||||
* Similarly, we avoid claiming an SRCU read lock held if the current
|
||||
* Similarly, we avoid claiming an RCU read lock held if the current
|
||||
* CPU is offline.
|
||||
*/
|
||||
static bool rcu_read_lock_held_common(bool *ret)
|
||||
{
|
||||
if (!debug_lockdep_rcu_enabled()) {
|
||||
*ret = 1;
|
||||
return true;
|
||||
}
|
||||
if (!rcu_is_watching()) {
|
||||
*ret = 0;
|
||||
return true;
|
||||
}
|
||||
if (!rcu_lockdep_current_cpu_online()) {
|
||||
*ret = 0;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int rcu_read_lock_sched_held(void)
|
||||
{
|
||||
int lockdep_opinion = 0;
|
||||
bool ret;
|
||||
|
||||
if (!debug_lockdep_rcu_enabled())
|
||||
return 1;
|
||||
if (!rcu_is_watching())
|
||||
return 0;
|
||||
if (!rcu_lockdep_current_cpu_online())
|
||||
return 0;
|
||||
if (debug_locks)
|
||||
lockdep_opinion = lock_is_held(&rcu_sched_lock_map);
|
||||
return lockdep_opinion || !preemptible();
|
||||
if (rcu_read_lock_held_common(&ret))
|
||||
return ret;
|
||||
return lock_is_held(&rcu_sched_lock_map) || !preemptible();
|
||||
}
|
||||
EXPORT_SYMBOL(rcu_read_lock_sched_held);
|
||||
#endif
|
||||
|
|
@ -136,8 +152,7 @@ static atomic_t rcu_expedited_nesting = ATOMIC_INIT(1);
|
|||
*/
|
||||
bool rcu_gp_is_expedited(void)
|
||||
{
|
||||
return rcu_expedited || atomic_read(&rcu_expedited_nesting) ||
|
||||
rcu_scheduler_active == RCU_SCHEDULER_INIT;
|
||||
return rcu_expedited || atomic_read(&rcu_expedited_nesting);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rcu_gp_is_expedited);
|
||||
|
||||
|
|
@ -261,12 +276,10 @@ NOKPROBE_SYMBOL(debug_lockdep_rcu_enabled);
|
|||
*/
|
||||
int rcu_read_lock_held(void)
|
||||
{
|
||||
if (!debug_lockdep_rcu_enabled())
|
||||
return 1;
|
||||
if (!rcu_is_watching())
|
||||
return 0;
|
||||
if (!rcu_lockdep_current_cpu_online())
|
||||
return 0;
|
||||
bool ret;
|
||||
|
||||
if (rcu_read_lock_held_common(&ret))
|
||||
return ret;
|
||||
return lock_is_held(&rcu_lock_map);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rcu_read_lock_held);
|
||||
|
|
@ -288,16 +301,28 @@ EXPORT_SYMBOL_GPL(rcu_read_lock_held);
|
|||
*/
|
||||
int rcu_read_lock_bh_held(void)
|
||||
{
|
||||
if (!debug_lockdep_rcu_enabled())
|
||||
return 1;
|
||||
if (!rcu_is_watching())
|
||||
return 0;
|
||||
if (!rcu_lockdep_current_cpu_online())
|
||||
return 0;
|
||||
bool ret;
|
||||
|
||||
if (rcu_read_lock_held_common(&ret))
|
||||
return ret;
|
||||
return in_softirq() || irqs_disabled();
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rcu_read_lock_bh_held);
|
||||
|
||||
int rcu_read_lock_any_held(void)
|
||||
{
|
||||
bool ret;
|
||||
|
||||
if (rcu_read_lock_held_common(&ret))
|
||||
return ret;
|
||||
if (lock_is_held(&rcu_lock_map) ||
|
||||
lock_is_held(&rcu_bh_lock_map) ||
|
||||
lock_is_held(&rcu_sched_lock_map))
|
||||
return 1;
|
||||
return !preemptible();
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rcu_read_lock_any_held);
|
||||
|
||||
#endif /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */
|
||||
|
||||
/**
|
||||
|
|
@ -437,6 +462,8 @@ EXPORT_SYMBOL_GPL(rcutorture_sched_setaffinity);
|
|||
#endif
|
||||
|
||||
#ifdef CONFIG_RCU_STALL_COMMON
|
||||
int rcu_cpu_stall_ftrace_dump __read_mostly;
|
||||
module_param(rcu_cpu_stall_ftrace_dump, int, 0644);
|
||||
int rcu_cpu_stall_suppress __read_mostly; /* 1 = suppress stall warnings. */
|
||||
EXPORT_SYMBOL_GPL(rcu_cpu_stall_suppress);
|
||||
module_param(rcu_cpu_stall_suppress, int, 0644);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -117,6 +117,7 @@ static void sugov_fast_switch(struct sugov_policy *sg_policy, u64 time,
|
|||
unsigned int next_freq)
|
||||
{
|
||||
struct cpufreq_policy *policy = sg_policy->policy;
|
||||
int cpu;
|
||||
|
||||
if (!sugov_update_next_freq(sg_policy, time, next_freq))
|
||||
return;
|
||||
|
|
@ -126,7 +127,11 @@ static void sugov_fast_switch(struct sugov_policy *sg_policy, u64 time,
|
|||
return;
|
||||
|
||||
policy->cur = next_freq;
|
||||
trace_cpu_frequency(next_freq, smp_processor_id());
|
||||
|
||||
if (trace_cpu_frequency_enabled()) {
|
||||
for_each_cpu(cpu, policy->cpus)
|
||||
trace_cpu_frequency(next_freq, cpu);
|
||||
}
|
||||
}
|
||||
|
||||
static void sugov_deferred_update(struct sugov_policy *sg_policy, u64 time,
|
||||
|
|
@ -263,9 +268,9 @@ unsigned long schedutil_cpu_util(int cpu, unsigned long util_cfs,
|
|||
* irq metric. Because IRQ/steal time is hidden from the task clock we
|
||||
* need to scale the task numbers:
|
||||
*
|
||||
* 1 - irq
|
||||
* U' = irq + ------- * U
|
||||
* max
|
||||
* max - irq
|
||||
* U' = irq + --------- * U
|
||||
* max
|
||||
*/
|
||||
util = scale_irq_capacity(util, irq, max);
|
||||
util += irq;
|
||||
|
|
|
|||
|
|
@ -287,7 +287,7 @@ static void task_non_contending(struct task_struct *p)
|
|||
|
||||
dl_se->dl_non_contending = 1;
|
||||
get_task_struct(p);
|
||||
hrtimer_start(timer, ns_to_ktime(zerolag_time), HRTIMER_MODE_REL);
|
||||
hrtimer_start(timer, ns_to_ktime(zerolag_time), HRTIMER_MODE_REL_HARD);
|
||||
}
|
||||
|
||||
static void task_contending(struct sched_dl_entity *dl_se, int flags)
|
||||
|
|
@ -529,6 +529,7 @@ static struct rq *find_lock_later_rq(struct task_struct *task, struct rq *rq);
|
|||
static struct rq *dl_task_offline_migration(struct rq *rq, struct task_struct *p)
|
||||
{
|
||||
struct rq *later_rq = NULL;
|
||||
struct dl_bw *dl_b;
|
||||
|
||||
later_rq = find_lock_later_rq(p, rq);
|
||||
if (!later_rq) {
|
||||
|
|
@ -557,6 +558,38 @@ static struct rq *dl_task_offline_migration(struct rq *rq, struct task_struct *p
|
|||
double_lock_balance(rq, later_rq);
|
||||
}
|
||||
|
||||
if (p->dl.dl_non_contending || p->dl.dl_throttled) {
|
||||
/*
|
||||
* Inactive timer is armed (or callback is running, but
|
||||
* waiting for us to release rq locks). In any case, when it
|
||||
* will fire (or continue), it will see running_bw of this
|
||||
* task migrated to later_rq (and correctly handle it).
|
||||
*/
|
||||
sub_running_bw(&p->dl, &rq->dl);
|
||||
sub_rq_bw(&p->dl, &rq->dl);
|
||||
|
||||
add_rq_bw(&p->dl, &later_rq->dl);
|
||||
add_running_bw(&p->dl, &later_rq->dl);
|
||||
} else {
|
||||
sub_rq_bw(&p->dl, &rq->dl);
|
||||
add_rq_bw(&p->dl, &later_rq->dl);
|
||||
}
|
||||
|
||||
/*
|
||||
* And we finally need to fixup root_domain(s) bandwidth accounting,
|
||||
* since p is still hanging out in the old (now moved to default) root
|
||||
* domain.
|
||||
*/
|
||||
dl_b = &rq->rd->dl_bw;
|
||||
raw_spin_lock(&dl_b->lock);
|
||||
__dl_sub(dl_b, p->dl.dl_bw, cpumask_weight(rq->rd->span));
|
||||
raw_spin_unlock(&dl_b->lock);
|
||||
|
||||
dl_b = &later_rq->rd->dl_bw;
|
||||
raw_spin_lock(&dl_b->lock);
|
||||
__dl_add(dl_b, p->dl.dl_bw, cpumask_weight(later_rq->rd->span));
|
||||
raw_spin_unlock(&dl_b->lock);
|
||||
|
||||
set_task_cpu(p, later_rq->cpu);
|
||||
double_unlock_balance(later_rq, rq);
|
||||
|
||||
|
|
@ -923,7 +956,7 @@ static int start_dl_timer(struct task_struct *p)
|
|||
*/
|
||||
if (!hrtimer_is_queued(timer)) {
|
||||
get_task_struct(p);
|
||||
hrtimer_start(timer, act, HRTIMER_MODE_ABS);
|
||||
hrtimer_start(timer, act, HRTIMER_MODE_ABS_HARD);
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
|
@ -1053,7 +1086,7 @@ void init_dl_task_timer(struct sched_dl_entity *dl_se)
|
|||
{
|
||||
struct hrtimer *timer = &dl_se->dl_timer;
|
||||
|
||||
hrtimer_init(timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
hrtimer_init(timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_HARD);
|
||||
timer->function = dl_task_timer;
|
||||
}
|
||||
|
||||
|
|
@ -1292,7 +1325,7 @@ void init_dl_inactive_task_timer(struct sched_dl_entity *dl_se)
|
|||
{
|
||||
struct hrtimer *timer = &dl_se->inactive_timer;
|
||||
|
||||
hrtimer_init(timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
hrtimer_init(timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_HARD);
|
||||
timer->function = inactive_task_timer;
|
||||
}
|
||||
|
||||
|
|
@ -1694,12 +1727,20 @@ static void start_hrtick_dl(struct rq *rq, struct task_struct *p)
|
|||
}
|
||||
#endif
|
||||
|
||||
static inline void set_next_task(struct rq *rq, struct task_struct *p)
|
||||
static void set_next_task_dl(struct rq *rq, struct task_struct *p)
|
||||
{
|
||||
p->se.exec_start = rq_clock_task(rq);
|
||||
|
||||
/* You can't push away the running task */
|
||||
dequeue_pushable_dl_task(rq, p);
|
||||
|
||||
if (hrtick_enabled(rq))
|
||||
start_hrtick_dl(rq, p);
|
||||
|
||||
if (rq->curr->sched_class != &dl_sched_class)
|
||||
update_dl_rq_load_avg(rq_clock_pelt(rq), rq, 0);
|
||||
|
||||
deadline_queue_push_tasks(rq);
|
||||
}
|
||||
|
||||
static struct sched_dl_entity *pick_next_dl_entity(struct rq *rq,
|
||||
|
|
@ -1720,64 +1761,42 @@ pick_next_task_dl(struct rq *rq, struct task_struct *prev, struct rq_flags *rf)
|
|||
struct task_struct *p;
|
||||
struct dl_rq *dl_rq;
|
||||
|
||||
WARN_ON_ONCE(prev || rf);
|
||||
|
||||
dl_rq = &rq->dl;
|
||||
|
||||
if (need_pull_dl_task(rq, prev)) {
|
||||
/*
|
||||
* This is OK, because current is on_cpu, which avoids it being
|
||||
* picked for load-balance and preemption/IRQs are still
|
||||
* disabled avoiding further scheduler activity on it and we're
|
||||
* being very careful to re-start the picking loop.
|
||||
*/
|
||||
rq_unpin_lock(rq, rf);
|
||||
pull_dl_task(rq);
|
||||
rq_repin_lock(rq, rf);
|
||||
/*
|
||||
* pull_dl_task() can drop (and re-acquire) rq->lock; this
|
||||
* means a stop task can slip in, in which case we need to
|
||||
* re-start task selection.
|
||||
*/
|
||||
if (rq->stop && task_on_rq_queued(rq->stop))
|
||||
return RETRY_TASK;
|
||||
}
|
||||
|
||||
/*
|
||||
* When prev is DL, we may throttle it in put_prev_task().
|
||||
* So, we update time before we check for dl_nr_running.
|
||||
*/
|
||||
if (prev->sched_class == &dl_sched_class)
|
||||
update_curr_dl(rq);
|
||||
|
||||
if (unlikely(!dl_rq->dl_nr_running))
|
||||
return NULL;
|
||||
|
||||
put_prev_task(rq, prev);
|
||||
|
||||
dl_se = pick_next_dl_entity(rq, dl_rq);
|
||||
BUG_ON(!dl_se);
|
||||
|
||||
p = dl_task_of(dl_se);
|
||||
|
||||
set_next_task(rq, p);
|
||||
|
||||
if (hrtick_enabled(rq))
|
||||
start_hrtick_dl(rq, p);
|
||||
|
||||
deadline_queue_push_tasks(rq);
|
||||
|
||||
if (rq->curr->sched_class != &dl_sched_class)
|
||||
update_dl_rq_load_avg(rq_clock_pelt(rq), rq, 0);
|
||||
set_next_task_dl(rq, p);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
static void put_prev_task_dl(struct rq *rq, struct task_struct *p)
|
||||
static void put_prev_task_dl(struct rq *rq, struct task_struct *p, struct rq_flags *rf)
|
||||
{
|
||||
update_curr_dl(rq);
|
||||
|
||||
update_dl_rq_load_avg(rq_clock_pelt(rq), rq, 1);
|
||||
if (on_dl_rq(&p->dl) && p->nr_cpus_allowed > 1)
|
||||
enqueue_pushable_dl_task(rq, p);
|
||||
|
||||
if (rf && !on_dl_rq(&p->dl) && need_pull_dl_task(rq, p)) {
|
||||
/*
|
||||
* This is OK, because current is on_cpu, which avoids it being
|
||||
* picked for load-balance and preemption/IRQs are still
|
||||
* disabled avoiding further scheduler activity on it and we've
|
||||
* not yet started the picking loop.
|
||||
*/
|
||||
rq_unpin_lock(rq, rf);
|
||||
pull_dl_task(rq);
|
||||
rq_repin_lock(rq, rf);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1811,11 +1830,6 @@ static void task_fork_dl(struct task_struct *p)
|
|||
*/
|
||||
}
|
||||
|
||||
static void set_curr_task_dl(struct rq *rq)
|
||||
{
|
||||
set_next_task(rq, rq->curr);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
|
||||
/* Only try algorithms three times */
|
||||
|
|
@ -2275,6 +2289,36 @@ void __init init_sched_dl_class(void)
|
|||
GFP_KERNEL, cpu_to_node(i));
|
||||
}
|
||||
|
||||
void dl_add_task_root_domain(struct task_struct *p)
|
||||
{
|
||||
struct rq_flags rf;
|
||||
struct rq *rq;
|
||||
struct dl_bw *dl_b;
|
||||
|
||||
rq = task_rq_lock(p, &rf);
|
||||
if (!dl_task(p))
|
||||
goto unlock;
|
||||
|
||||
dl_b = &rq->rd->dl_bw;
|
||||
raw_spin_lock(&dl_b->lock);
|
||||
|
||||
__dl_add(dl_b, p->dl.dl_bw, cpumask_weight(rq->rd->span));
|
||||
|
||||
raw_spin_unlock(&dl_b->lock);
|
||||
|
||||
unlock:
|
||||
task_rq_unlock(rq, p, &rf);
|
||||
}
|
||||
|
||||
void dl_clear_root_domain(struct root_domain *rd)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
raw_spin_lock_irqsave(&rd->dl_bw.lock, flags);
|
||||
rd->dl_bw.total_bw = 0;
|
||||
raw_spin_unlock_irqrestore(&rd->dl_bw.lock, flags);
|
||||
}
|
||||
|
||||
#endif /* CONFIG_SMP */
|
||||
|
||||
static void switched_from_dl(struct rq *rq, struct task_struct *p)
|
||||
|
|
@ -2395,6 +2439,7 @@ const struct sched_class dl_sched_class = {
|
|||
|
||||
.pick_next_task = pick_next_task_dl,
|
||||
.put_prev_task = put_prev_task_dl,
|
||||
.set_next_task = set_next_task_dl,
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
.select_task_rq = select_task_rq_dl,
|
||||
|
|
@ -2405,7 +2450,6 @@ const struct sched_class dl_sched_class = {
|
|||
.task_woken = task_woken_dl,
|
||||
#endif
|
||||
|
||||
.set_curr_task = set_curr_task_dl,
|
||||
.task_tick = task_tick_dl,
|
||||
.task_fork = task_fork_dl,
|
||||
|
||||
|
|
|
|||
|
|
@ -96,12 +96,12 @@ int __weak arch_asym_cpu_priority(int cpu)
|
|||
}
|
||||
|
||||
/*
|
||||
* The margin used when comparing utilization with CPU capacity:
|
||||
* util * margin < capacity * 1024
|
||||
* The margin used when comparing utilization with CPU capacity.
|
||||
*
|
||||
* (default: ~20%)
|
||||
*/
|
||||
static unsigned int capacity_margin = 1280;
|
||||
#define fits_capacity(cap, max) ((cap) * 1280 < (max) * 1024)
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_CFS_BANDWIDTH
|
||||
|
|
@ -1188,47 +1188,6 @@ static unsigned int task_scan_max(struct task_struct *p)
|
|||
return max(smin, smax);
|
||||
}
|
||||
|
||||
void init_numa_balancing(unsigned long clone_flags, struct task_struct *p)
|
||||
{
|
||||
int mm_users = 0;
|
||||
struct mm_struct *mm = p->mm;
|
||||
|
||||
if (mm) {
|
||||
mm_users = atomic_read(&mm->mm_users);
|
||||
if (mm_users == 1) {
|
||||
mm->numa_next_scan = jiffies + msecs_to_jiffies(sysctl_numa_balancing_scan_delay);
|
||||
mm->numa_scan_seq = 0;
|
||||
}
|
||||
}
|
||||
p->node_stamp = 0;
|
||||
p->numa_scan_seq = mm ? mm->numa_scan_seq : 0;
|
||||
p->numa_scan_period = sysctl_numa_balancing_scan_delay;
|
||||
p->numa_work.next = &p->numa_work;
|
||||
p->numa_faults = NULL;
|
||||
RCU_INIT_POINTER(p->numa_group, NULL);
|
||||
p->last_task_numa_placement = 0;
|
||||
p->last_sum_exec_runtime = 0;
|
||||
|
||||
/* New address space, reset the preferred nid */
|
||||
if (!(clone_flags & CLONE_VM)) {
|
||||
p->numa_preferred_nid = NUMA_NO_NODE;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* New thread, keep existing numa_preferred_nid which should be copied
|
||||
* already by arch_dup_task_struct but stagger when scans start.
|
||||
*/
|
||||
if (mm) {
|
||||
unsigned int delay;
|
||||
|
||||
delay = min_t(unsigned int, task_scan_max(current),
|
||||
current->numa_scan_period * mm_users * NSEC_PER_MSEC);
|
||||
delay += 2 * TICK_NSEC;
|
||||
p->node_stamp = delay;
|
||||
}
|
||||
}
|
||||
|
||||
static void account_numa_enqueue(struct rq *rq, struct task_struct *p)
|
||||
{
|
||||
rq->nr_numa_running += (p->numa_preferred_nid != NUMA_NO_NODE);
|
||||
|
|
@ -2523,7 +2482,7 @@ static void reset_ptenuma_scan(struct task_struct *p)
|
|||
* The expensive part of numa migration is done from task_work context.
|
||||
* Triggered from task_tick_numa().
|
||||
*/
|
||||
void task_numa_work(struct callback_head *work)
|
||||
static void task_numa_work(struct callback_head *work)
|
||||
{
|
||||
unsigned long migrate, next_scan, now = jiffies;
|
||||
struct task_struct *p = current;
|
||||
|
|
@ -2536,7 +2495,7 @@ void task_numa_work(struct callback_head *work)
|
|||
|
||||
SCHED_WARN_ON(p != container_of(work, struct task_struct, numa_work));
|
||||
|
||||
work->next = work; /* protect against double add */
|
||||
work->next = work;
|
||||
/*
|
||||
* Who cares about NUMA placement when they're dying.
|
||||
*
|
||||
|
|
@ -2665,6 +2624,50 @@ out:
|
|||
}
|
||||
}
|
||||
|
||||
void init_numa_balancing(unsigned long clone_flags, struct task_struct *p)
|
||||
{
|
||||
int mm_users = 0;
|
||||
struct mm_struct *mm = p->mm;
|
||||
|
||||
if (mm) {
|
||||
mm_users = atomic_read(&mm->mm_users);
|
||||
if (mm_users == 1) {
|
||||
mm->numa_next_scan = jiffies + msecs_to_jiffies(sysctl_numa_balancing_scan_delay);
|
||||
mm->numa_scan_seq = 0;
|
||||
}
|
||||
}
|
||||
p->node_stamp = 0;
|
||||
p->numa_scan_seq = mm ? mm->numa_scan_seq : 0;
|
||||
p->numa_scan_period = sysctl_numa_balancing_scan_delay;
|
||||
/* Protect against double add, see task_tick_numa and task_numa_work */
|
||||
p->numa_work.next = &p->numa_work;
|
||||
p->numa_faults = NULL;
|
||||
RCU_INIT_POINTER(p->numa_group, NULL);
|
||||
p->last_task_numa_placement = 0;
|
||||
p->last_sum_exec_runtime = 0;
|
||||
|
||||
init_task_work(&p->numa_work, task_numa_work);
|
||||
|
||||
/* New address space, reset the preferred nid */
|
||||
if (!(clone_flags & CLONE_VM)) {
|
||||
p->numa_preferred_nid = NUMA_NO_NODE;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* New thread, keep existing numa_preferred_nid which should be copied
|
||||
* already by arch_dup_task_struct but stagger when scans start.
|
||||
*/
|
||||
if (mm) {
|
||||
unsigned int delay;
|
||||
|
||||
delay = min_t(unsigned int, task_scan_max(current),
|
||||
current->numa_scan_period * mm_users * NSEC_PER_MSEC);
|
||||
delay += 2 * TICK_NSEC;
|
||||
p->node_stamp = delay;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Drive the periodic memory faults..
|
||||
*/
|
||||
|
|
@ -2693,10 +2696,8 @@ static void task_tick_numa(struct rq *rq, struct task_struct *curr)
|
|||
curr->numa_scan_period = task_scan_start(curr);
|
||||
curr->node_stamp += period;
|
||||
|
||||
if (!time_before(jiffies, curr->mm->numa_next_scan)) {
|
||||
init_task_work(work, task_numa_work); /* TODO: move this into sched_fork() */
|
||||
if (!time_before(jiffies, curr->mm->numa_next_scan))
|
||||
task_work_add(curr, work, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -3689,8 +3690,6 @@ static inline unsigned long cfs_rq_load_avg(struct cfs_rq *cfs_rq)
|
|||
return cfs_rq->avg.load_avg;
|
||||
}
|
||||
|
||||
static int idle_balance(struct rq *this_rq, struct rq_flags *rf);
|
||||
|
||||
static inline unsigned long task_util(struct task_struct *p)
|
||||
{
|
||||
return READ_ONCE(p->se.avg.util_avg);
|
||||
|
|
@ -3807,7 +3806,7 @@ util_est_dequeue(struct cfs_rq *cfs_rq, struct task_struct *p, bool task_sleep)
|
|||
|
||||
static inline int task_fits_capacity(struct task_struct *p, long capacity)
|
||||
{
|
||||
return capacity * 1024 > task_util_est(p) * capacity_margin;
|
||||
return fits_capacity(task_util_est(p), capacity);
|
||||
}
|
||||
|
||||
static inline void update_misfit_status(struct task_struct *p, struct rq *rq)
|
||||
|
|
@ -4370,8 +4369,6 @@ void __refill_cfs_bandwidth_runtime(struct cfs_bandwidth *cfs_b)
|
|||
|
||||
now = sched_clock_cpu(smp_processor_id());
|
||||
cfs_b->runtime = cfs_b->quota;
|
||||
cfs_b->runtime_expires = now + ktime_to_ns(cfs_b->period);
|
||||
cfs_b->expires_seq++;
|
||||
}
|
||||
|
||||
static inline struct cfs_bandwidth *tg_cfs_bandwidth(struct task_group *tg)
|
||||
|
|
@ -4393,8 +4390,7 @@ static int assign_cfs_rq_runtime(struct cfs_rq *cfs_rq)
|
|||
{
|
||||
struct task_group *tg = cfs_rq->tg;
|
||||
struct cfs_bandwidth *cfs_b = tg_cfs_bandwidth(tg);
|
||||
u64 amount = 0, min_amount, expires;
|
||||
int expires_seq;
|
||||
u64 amount = 0, min_amount;
|
||||
|
||||
/* note: this is a positive sum as runtime_remaining <= 0 */
|
||||
min_amount = sched_cfs_bandwidth_slice() - cfs_rq->runtime_remaining;
|
||||
|
|
@ -4411,61 +4407,17 @@ static int assign_cfs_rq_runtime(struct cfs_rq *cfs_rq)
|
|||
cfs_b->idle = 0;
|
||||
}
|
||||
}
|
||||
expires_seq = cfs_b->expires_seq;
|
||||
expires = cfs_b->runtime_expires;
|
||||
raw_spin_unlock(&cfs_b->lock);
|
||||
|
||||
cfs_rq->runtime_remaining += amount;
|
||||
/*
|
||||
* we may have advanced our local expiration to account for allowed
|
||||
* spread between our sched_clock and the one on which runtime was
|
||||
* issued.
|
||||
*/
|
||||
if (cfs_rq->expires_seq != expires_seq) {
|
||||
cfs_rq->expires_seq = expires_seq;
|
||||
cfs_rq->runtime_expires = expires;
|
||||
}
|
||||
|
||||
return cfs_rq->runtime_remaining > 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note: This depends on the synchronization provided by sched_clock and the
|
||||
* fact that rq->clock snapshots this value.
|
||||
*/
|
||||
static void expire_cfs_rq_runtime(struct cfs_rq *cfs_rq)
|
||||
{
|
||||
struct cfs_bandwidth *cfs_b = tg_cfs_bandwidth(cfs_rq->tg);
|
||||
|
||||
/* if the deadline is ahead of our clock, nothing to do */
|
||||
if (likely((s64)(rq_clock(rq_of(cfs_rq)) - cfs_rq->runtime_expires) < 0))
|
||||
return;
|
||||
|
||||
if (cfs_rq->runtime_remaining < 0)
|
||||
return;
|
||||
|
||||
/*
|
||||
* If the local deadline has passed we have to consider the
|
||||
* possibility that our sched_clock is 'fast' and the global deadline
|
||||
* has not truly expired.
|
||||
*
|
||||
* Fortunately we can check determine whether this the case by checking
|
||||
* whether the global deadline(cfs_b->expires_seq) has advanced.
|
||||
*/
|
||||
if (cfs_rq->expires_seq == cfs_b->expires_seq) {
|
||||
/* extend local deadline, drift is bounded above by 2 ticks */
|
||||
cfs_rq->runtime_expires += TICK_NSEC;
|
||||
} else {
|
||||
/* global deadline is ahead, expiration has passed */
|
||||
cfs_rq->runtime_remaining = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void __account_cfs_rq_runtime(struct cfs_rq *cfs_rq, u64 delta_exec)
|
||||
{
|
||||
/* dock delta_exec before expiring quota (as it could span periods) */
|
||||
cfs_rq->runtime_remaining -= delta_exec;
|
||||
expire_cfs_rq_runtime(cfs_rq);
|
||||
|
||||
if (likely(cfs_rq->runtime_remaining > 0))
|
||||
return;
|
||||
|
|
@ -4556,7 +4508,7 @@ static void throttle_cfs_rq(struct cfs_rq *cfs_rq)
|
|||
struct rq *rq = rq_of(cfs_rq);
|
||||
struct cfs_bandwidth *cfs_b = tg_cfs_bandwidth(cfs_rq->tg);
|
||||
struct sched_entity *se;
|
||||
long task_delta, dequeue = 1;
|
||||
long task_delta, idle_task_delta, dequeue = 1;
|
||||
bool empty;
|
||||
|
||||
se = cfs_rq->tg->se[cpu_of(rq_of(cfs_rq))];
|
||||
|
|
@ -4567,6 +4519,7 @@ static void throttle_cfs_rq(struct cfs_rq *cfs_rq)
|
|||
rcu_read_unlock();
|
||||
|
||||
task_delta = cfs_rq->h_nr_running;
|
||||
idle_task_delta = cfs_rq->idle_h_nr_running;
|
||||
for_each_sched_entity(se) {
|
||||
struct cfs_rq *qcfs_rq = cfs_rq_of(se);
|
||||
/* throttled entity or throttle-on-deactivate */
|
||||
|
|
@ -4576,6 +4529,7 @@ static void throttle_cfs_rq(struct cfs_rq *cfs_rq)
|
|||
if (dequeue)
|
||||
dequeue_entity(qcfs_rq, se, DEQUEUE_SLEEP);
|
||||
qcfs_rq->h_nr_running -= task_delta;
|
||||
qcfs_rq->idle_h_nr_running -= idle_task_delta;
|
||||
|
||||
if (qcfs_rq->load.weight)
|
||||
dequeue = 0;
|
||||
|
|
@ -4615,7 +4569,7 @@ void unthrottle_cfs_rq(struct cfs_rq *cfs_rq)
|
|||
struct cfs_bandwidth *cfs_b = tg_cfs_bandwidth(cfs_rq->tg);
|
||||
struct sched_entity *se;
|
||||
int enqueue = 1;
|
||||
long task_delta;
|
||||
long task_delta, idle_task_delta;
|
||||
|
||||
se = cfs_rq->tg->se[cpu_of(rq)];
|
||||
|
||||
|
|
@ -4635,6 +4589,7 @@ void unthrottle_cfs_rq(struct cfs_rq *cfs_rq)
|
|||
return;
|
||||
|
||||
task_delta = cfs_rq->h_nr_running;
|
||||
idle_task_delta = cfs_rq->idle_h_nr_running;
|
||||
for_each_sched_entity(se) {
|
||||
if (se->on_rq)
|
||||
enqueue = 0;
|
||||
|
|
@ -4643,6 +4598,7 @@ void unthrottle_cfs_rq(struct cfs_rq *cfs_rq)
|
|||
if (enqueue)
|
||||
enqueue_entity(cfs_rq, se, ENQUEUE_WAKEUP);
|
||||
cfs_rq->h_nr_running += task_delta;
|
||||
cfs_rq->idle_h_nr_running += idle_task_delta;
|
||||
|
||||
if (cfs_rq_throttled(cfs_rq))
|
||||
break;
|
||||
|
|
@ -4658,8 +4614,7 @@ void unthrottle_cfs_rq(struct cfs_rq *cfs_rq)
|
|||
resched_curr(rq);
|
||||
}
|
||||
|
||||
static u64 distribute_cfs_runtime(struct cfs_bandwidth *cfs_b,
|
||||
u64 remaining, u64 expires)
|
||||
static u64 distribute_cfs_runtime(struct cfs_bandwidth *cfs_b, u64 remaining)
|
||||
{
|
||||
struct cfs_rq *cfs_rq;
|
||||
u64 runtime;
|
||||
|
|
@ -4684,7 +4639,6 @@ static u64 distribute_cfs_runtime(struct cfs_bandwidth *cfs_b,
|
|||
remaining -= runtime;
|
||||
|
||||
cfs_rq->runtime_remaining += runtime;
|
||||
cfs_rq->runtime_expires = expires;
|
||||
|
||||
/* we check whether we're throttled above */
|
||||
if (cfs_rq->runtime_remaining > 0)
|
||||
|
|
@ -4709,7 +4663,7 @@ next:
|
|||
*/
|
||||
static int do_sched_cfs_period_timer(struct cfs_bandwidth *cfs_b, int overrun, unsigned long flags)
|
||||
{
|
||||
u64 runtime, runtime_expires;
|
||||
u64 runtime;
|
||||
int throttled;
|
||||
|
||||
/* no need to continue the timer with no bandwidth constraint */
|
||||
|
|
@ -4737,8 +4691,6 @@ static int do_sched_cfs_period_timer(struct cfs_bandwidth *cfs_b, int overrun, u
|
|||
/* account preceding periods in which throttling occurred */
|
||||
cfs_b->nr_throttled += overrun;
|
||||
|
||||
runtime_expires = cfs_b->runtime_expires;
|
||||
|
||||
/*
|
||||
* This check is repeated as we are holding onto the new bandwidth while
|
||||
* we unthrottle. This can potentially race with an unthrottled group
|
||||
|
|
@ -4751,8 +4703,7 @@ static int do_sched_cfs_period_timer(struct cfs_bandwidth *cfs_b, int overrun, u
|
|||
cfs_b->distribute_running = 1;
|
||||
raw_spin_unlock_irqrestore(&cfs_b->lock, flags);
|
||||
/* we can't nest cfs_b->lock while distributing bandwidth */
|
||||
runtime = distribute_cfs_runtime(cfs_b, runtime,
|
||||
runtime_expires);
|
||||
runtime = distribute_cfs_runtime(cfs_b, runtime);
|
||||
raw_spin_lock_irqsave(&cfs_b->lock, flags);
|
||||
|
||||
cfs_b->distribute_running = 0;
|
||||
|
|
@ -4834,8 +4785,7 @@ static void __return_cfs_rq_runtime(struct cfs_rq *cfs_rq)
|
|||
return;
|
||||
|
||||
raw_spin_lock(&cfs_b->lock);
|
||||
if (cfs_b->quota != RUNTIME_INF &&
|
||||
cfs_rq->runtime_expires == cfs_b->runtime_expires) {
|
||||
if (cfs_b->quota != RUNTIME_INF) {
|
||||
cfs_b->runtime += slack_runtime;
|
||||
|
||||
/* we are under rq->lock, defer unthrottling using a timer */
|
||||
|
|
@ -4868,7 +4818,6 @@ static void do_sched_cfs_slack_timer(struct cfs_bandwidth *cfs_b)
|
|||
{
|
||||
u64 runtime = 0, slice = sched_cfs_bandwidth_slice();
|
||||
unsigned long flags;
|
||||
u64 expires;
|
||||
|
||||
/* confirm we're still not at a refresh boundary */
|
||||
raw_spin_lock_irqsave(&cfs_b->lock, flags);
|
||||
|
|
@ -4886,7 +4835,6 @@ static void do_sched_cfs_slack_timer(struct cfs_bandwidth *cfs_b)
|
|||
if (cfs_b->quota != RUNTIME_INF && cfs_b->runtime > slice)
|
||||
runtime = cfs_b->runtime;
|
||||
|
||||
expires = cfs_b->runtime_expires;
|
||||
if (runtime)
|
||||
cfs_b->distribute_running = 1;
|
||||
|
||||
|
|
@ -4895,11 +4843,10 @@ static void do_sched_cfs_slack_timer(struct cfs_bandwidth *cfs_b)
|
|||
if (!runtime)
|
||||
return;
|
||||
|
||||
runtime = distribute_cfs_runtime(cfs_b, runtime, expires);
|
||||
runtime = distribute_cfs_runtime(cfs_b, runtime);
|
||||
|
||||
raw_spin_lock_irqsave(&cfs_b->lock, flags);
|
||||
if (expires == cfs_b->runtime_expires)
|
||||
lsub_positive(&cfs_b->runtime, runtime);
|
||||
lsub_positive(&cfs_b->runtime, runtime);
|
||||
cfs_b->distribute_running = 0;
|
||||
raw_spin_unlock_irqrestore(&cfs_b->lock, flags);
|
||||
}
|
||||
|
|
@ -5056,8 +5003,6 @@ void start_cfs_bandwidth(struct cfs_bandwidth *cfs_b)
|
|||
|
||||
cfs_b->period_active = 1;
|
||||
overrun = hrtimer_forward_now(&cfs_b->period_timer, cfs_b->period);
|
||||
cfs_b->runtime_expires += (overrun + 1) * ktime_to_ns(cfs_b->period);
|
||||
cfs_b->expires_seq++;
|
||||
hrtimer_start_expires(&cfs_b->period_timer, HRTIMER_MODE_ABS_PINNED);
|
||||
}
|
||||
|
||||
|
|
@ -5235,7 +5180,7 @@ static inline unsigned long cpu_util(int cpu);
|
|||
|
||||
static inline bool cpu_overutilized(int cpu)
|
||||
{
|
||||
return (capacity_of(cpu) * 1024) < (cpu_util(cpu) * capacity_margin);
|
||||
return !fits_capacity(cpu_util(cpu), capacity_of(cpu));
|
||||
}
|
||||
|
||||
static inline void update_overutilized_status(struct rq *rq)
|
||||
|
|
@ -5259,6 +5204,7 @@ enqueue_task_fair(struct rq *rq, struct task_struct *p, int flags)
|
|||
{
|
||||
struct cfs_rq *cfs_rq;
|
||||
struct sched_entity *se = &p->se;
|
||||
int idle_h_nr_running = task_has_idle_policy(p);
|
||||
|
||||
/*
|
||||
* The code below (indirectly) updates schedutil which looks at
|
||||
|
|
@ -5291,6 +5237,7 @@ enqueue_task_fair(struct rq *rq, struct task_struct *p, int flags)
|
|||
if (cfs_rq_throttled(cfs_rq))
|
||||
break;
|
||||
cfs_rq->h_nr_running++;
|
||||
cfs_rq->idle_h_nr_running += idle_h_nr_running;
|
||||
|
||||
flags = ENQUEUE_WAKEUP;
|
||||
}
|
||||
|
|
@ -5298,6 +5245,7 @@ enqueue_task_fair(struct rq *rq, struct task_struct *p, int flags)
|
|||
for_each_sched_entity(se) {
|
||||
cfs_rq = cfs_rq_of(se);
|
||||
cfs_rq->h_nr_running++;
|
||||
cfs_rq->idle_h_nr_running += idle_h_nr_running;
|
||||
|
||||
if (cfs_rq_throttled(cfs_rq))
|
||||
break;
|
||||
|
|
@ -5359,6 +5307,7 @@ static void dequeue_task_fair(struct rq *rq, struct task_struct *p, int flags)
|
|||
struct cfs_rq *cfs_rq;
|
||||
struct sched_entity *se = &p->se;
|
||||
int task_sleep = flags & DEQUEUE_SLEEP;
|
||||
int idle_h_nr_running = task_has_idle_policy(p);
|
||||
|
||||
for_each_sched_entity(se) {
|
||||
cfs_rq = cfs_rq_of(se);
|
||||
|
|
@ -5373,6 +5322,7 @@ static void dequeue_task_fair(struct rq *rq, struct task_struct *p, int flags)
|
|||
if (cfs_rq_throttled(cfs_rq))
|
||||
break;
|
||||
cfs_rq->h_nr_running--;
|
||||
cfs_rq->idle_h_nr_running -= idle_h_nr_running;
|
||||
|
||||
/* Don't dequeue parent if it has other entities besides us */
|
||||
if (cfs_rq->load.weight) {
|
||||
|
|
@ -5392,6 +5342,7 @@ static void dequeue_task_fair(struct rq *rq, struct task_struct *p, int flags)
|
|||
for_each_sched_entity(se) {
|
||||
cfs_rq = cfs_rq_of(se);
|
||||
cfs_rq->h_nr_running--;
|
||||
cfs_rq->idle_h_nr_running -= idle_h_nr_running;
|
||||
|
||||
if (cfs_rq_throttled(cfs_rq))
|
||||
break;
|
||||
|
|
@ -5425,6 +5376,15 @@ static struct {
|
|||
|
||||
#endif /* CONFIG_NO_HZ_COMMON */
|
||||
|
||||
/* CPU only has SCHED_IDLE tasks enqueued */
|
||||
static int sched_idle_cpu(int cpu)
|
||||
{
|
||||
struct rq *rq = cpu_rq(cpu);
|
||||
|
||||
return unlikely(rq->nr_running == rq->cfs.idle_h_nr_running &&
|
||||
rq->nr_running);
|
||||
}
|
||||
|
||||
static unsigned long cpu_runnable_load(struct rq *rq)
|
||||
{
|
||||
return cfs_rq_runnable_load_avg(&rq->cfs);
|
||||
|
|
@ -5747,7 +5707,7 @@ find_idlest_group_cpu(struct sched_group *group, struct task_struct *p, int this
|
|||
unsigned int min_exit_latency = UINT_MAX;
|
||||
u64 latest_idle_timestamp = 0;
|
||||
int least_loaded_cpu = this_cpu;
|
||||
int shallowest_idle_cpu = -1;
|
||||
int shallowest_idle_cpu = -1, si_cpu = -1;
|
||||
int i;
|
||||
|
||||
/* Check if we have any choice: */
|
||||
|
|
@ -5778,7 +5738,12 @@ find_idlest_group_cpu(struct sched_group *group, struct task_struct *p, int this
|
|||
latest_idle_timestamp = rq->idle_stamp;
|
||||
shallowest_idle_cpu = i;
|
||||
}
|
||||
} else if (shallowest_idle_cpu == -1) {
|
||||
} else if (shallowest_idle_cpu == -1 && si_cpu == -1) {
|
||||
if (sched_idle_cpu(i)) {
|
||||
si_cpu = i;
|
||||
continue;
|
||||
}
|
||||
|
||||
load = cpu_runnable_load(cpu_rq(i));
|
||||
if (load < min_load) {
|
||||
min_load = load;
|
||||
|
|
@ -5787,7 +5752,11 @@ find_idlest_group_cpu(struct sched_group *group, struct task_struct *p, int this
|
|||
}
|
||||
}
|
||||
|
||||
return shallowest_idle_cpu != -1 ? shallowest_idle_cpu : least_loaded_cpu;
|
||||
if (shallowest_idle_cpu != -1)
|
||||
return shallowest_idle_cpu;
|
||||
if (si_cpu != -1)
|
||||
return si_cpu;
|
||||
return least_loaded_cpu;
|
||||
}
|
||||
|
||||
static inline int find_idlest_cpu(struct sched_domain *sd, struct task_struct *p,
|
||||
|
|
@ -5940,7 +5909,7 @@ static int select_idle_core(struct task_struct *p, struct sched_domain *sd, int
|
|||
*/
|
||||
static int select_idle_smt(struct task_struct *p, int target)
|
||||
{
|
||||
int cpu;
|
||||
int cpu, si_cpu = -1;
|
||||
|
||||
if (!static_branch_likely(&sched_smt_present))
|
||||
return -1;
|
||||
|
|
@ -5950,9 +5919,11 @@ static int select_idle_smt(struct task_struct *p, int target)
|
|||
continue;
|
||||
if (available_idle_cpu(cpu))
|
||||
return cpu;
|
||||
if (si_cpu == -1 && sched_idle_cpu(cpu))
|
||||
si_cpu = cpu;
|
||||
}
|
||||
|
||||
return -1;
|
||||
return si_cpu;
|
||||
}
|
||||
|
||||
#else /* CONFIG_SCHED_SMT */
|
||||
|
|
@ -5980,8 +5951,8 @@ static int select_idle_cpu(struct task_struct *p, struct sched_domain *sd, int t
|
|||
u64 avg_cost, avg_idle;
|
||||
u64 time, cost;
|
||||
s64 delta;
|
||||
int cpu, nr = INT_MAX;
|
||||
int this = smp_processor_id();
|
||||
int cpu, nr = INT_MAX, si_cpu = -1;
|
||||
|
||||
this_sd = rcu_dereference(*this_cpu_ptr(&sd_llc));
|
||||
if (!this_sd)
|
||||
|
|
@ -6009,11 +5980,13 @@ static int select_idle_cpu(struct task_struct *p, struct sched_domain *sd, int t
|
|||
|
||||
for_each_cpu_wrap(cpu, sched_domain_span(sd), target) {
|
||||
if (!--nr)
|
||||
return -1;
|
||||
return si_cpu;
|
||||
if (!cpumask_test_cpu(cpu, p->cpus_ptr))
|
||||
continue;
|
||||
if (available_idle_cpu(cpu))
|
||||
break;
|
||||
if (si_cpu == -1 && sched_idle_cpu(cpu))
|
||||
si_cpu = cpu;
|
||||
}
|
||||
|
||||
time = cpu_clock(this) - time;
|
||||
|
|
@ -6032,13 +6005,14 @@ static int select_idle_sibling(struct task_struct *p, int prev, int target)
|
|||
struct sched_domain *sd;
|
||||
int i, recent_used_cpu;
|
||||
|
||||
if (available_idle_cpu(target))
|
||||
if (available_idle_cpu(target) || sched_idle_cpu(target))
|
||||
return target;
|
||||
|
||||
/*
|
||||
* If the previous CPU is cache affine and idle, don't be stupid:
|
||||
*/
|
||||
if (prev != target && cpus_share_cache(prev, target) && available_idle_cpu(prev))
|
||||
if (prev != target && cpus_share_cache(prev, target) &&
|
||||
(available_idle_cpu(prev) || sched_idle_cpu(prev)))
|
||||
return prev;
|
||||
|
||||
/* Check a recently used CPU as a potential idle candidate: */
|
||||
|
|
@ -6046,7 +6020,7 @@ static int select_idle_sibling(struct task_struct *p, int prev, int target)
|
|||
if (recent_used_cpu != prev &&
|
||||
recent_used_cpu != target &&
|
||||
cpus_share_cache(recent_used_cpu, target) &&
|
||||
available_idle_cpu(recent_used_cpu) &&
|
||||
(available_idle_cpu(recent_used_cpu) || sched_idle_cpu(recent_used_cpu)) &&
|
||||
cpumask_test_cpu(p->recent_used_cpu, p->cpus_ptr)) {
|
||||
/*
|
||||
* Replace recent_used_cpu with prev as it is a potential
|
||||
|
|
@ -6282,69 +6256,55 @@ static unsigned long cpu_util_next(int cpu, struct task_struct *p, int dst_cpu)
|
|||
}
|
||||
|
||||
/*
|
||||
* compute_energy(): Estimates the energy that would be consumed if @p was
|
||||
* compute_energy(): Estimates the energy that @pd would consume if @p was
|
||||
* migrated to @dst_cpu. compute_energy() predicts what will be the utilization
|
||||
* landscape of the * CPUs after the task migration, and uses the Energy Model
|
||||
* landscape of @pd's CPUs after the task migration, and uses the Energy Model
|
||||
* to compute what would be the energy if we decided to actually migrate that
|
||||
* task.
|
||||
*/
|
||||
static long
|
||||
compute_energy(struct task_struct *p, int dst_cpu, struct perf_domain *pd)
|
||||
{
|
||||
unsigned int max_util, util_cfs, cpu_util, cpu_cap;
|
||||
unsigned long sum_util, energy = 0;
|
||||
struct task_struct *tsk;
|
||||
struct cpumask *pd_mask = perf_domain_span(pd);
|
||||
unsigned long cpu_cap = arch_scale_cpu_capacity(cpumask_first(pd_mask));
|
||||
unsigned long max_util = 0, sum_util = 0;
|
||||
int cpu;
|
||||
|
||||
for (; pd; pd = pd->next) {
|
||||
struct cpumask *pd_mask = perf_domain_span(pd);
|
||||
/*
|
||||
* The capacity state of CPUs of the current rd can be driven by CPUs
|
||||
* of another rd if they belong to the same pd. So, account for the
|
||||
* utilization of these CPUs too by masking pd with cpu_online_mask
|
||||
* instead of the rd span.
|
||||
*
|
||||
* If an entire pd is outside of the current rd, it will not appear in
|
||||
* its pd list and will not be accounted by compute_energy().
|
||||
*/
|
||||
for_each_cpu_and(cpu, pd_mask, cpu_online_mask) {
|
||||
unsigned long cpu_util, util_cfs = cpu_util_next(cpu, p, dst_cpu);
|
||||
struct task_struct *tsk = cpu == dst_cpu ? p : NULL;
|
||||
|
||||
/*
|
||||
* The energy model mandates all the CPUs of a performance
|
||||
* domain have the same capacity.
|
||||
* Busy time computation: utilization clamping is not
|
||||
* required since the ratio (sum_util / cpu_capacity)
|
||||
* is already enough to scale the EM reported power
|
||||
* consumption at the (eventually clamped) cpu_capacity.
|
||||
*/
|
||||
cpu_cap = arch_scale_cpu_capacity(cpumask_first(pd_mask));
|
||||
max_util = sum_util = 0;
|
||||
sum_util += schedutil_cpu_util(cpu, util_cfs, cpu_cap,
|
||||
ENERGY_UTIL, NULL);
|
||||
|
||||
/*
|
||||
* The capacity state of CPUs of the current rd can be driven by
|
||||
* CPUs of another rd if they belong to the same performance
|
||||
* domain. So, account for the utilization of these CPUs too
|
||||
* by masking pd with cpu_online_mask instead of the rd span.
|
||||
*
|
||||
* If an entire performance domain is outside of the current rd,
|
||||
* it will not appear in its pd list and will not be accounted
|
||||
* by compute_energy().
|
||||
* Performance domain frequency: utilization clamping
|
||||
* must be considered since it affects the selection
|
||||
* of the performance domain frequency.
|
||||
* NOTE: in case RT tasks are running, by default the
|
||||
* FREQUENCY_UTIL's utilization can be max OPP.
|
||||
*/
|
||||
for_each_cpu_and(cpu, pd_mask, cpu_online_mask) {
|
||||
util_cfs = cpu_util_next(cpu, p, dst_cpu);
|
||||
|
||||
/*
|
||||
* Busy time computation: utilization clamping is not
|
||||
* required since the ratio (sum_util / cpu_capacity)
|
||||
* is already enough to scale the EM reported power
|
||||
* consumption at the (eventually clamped) cpu_capacity.
|
||||
*/
|
||||
sum_util += schedutil_cpu_util(cpu, util_cfs, cpu_cap,
|
||||
ENERGY_UTIL, NULL);
|
||||
|
||||
/*
|
||||
* Performance domain frequency: utilization clamping
|
||||
* must be considered since it affects the selection
|
||||
* of the performance domain frequency.
|
||||
* NOTE: in case RT tasks are running, by default the
|
||||
* FREQUENCY_UTIL's utilization can be max OPP.
|
||||
*/
|
||||
tsk = cpu == dst_cpu ? p : NULL;
|
||||
cpu_util = schedutil_cpu_util(cpu, util_cfs, cpu_cap,
|
||||
FREQUENCY_UTIL, tsk);
|
||||
max_util = max(max_util, cpu_util);
|
||||
}
|
||||
|
||||
energy += em_pd_energy(pd->em_pd, max_util, sum_util);
|
||||
cpu_util = schedutil_cpu_util(cpu, util_cfs, cpu_cap,
|
||||
FREQUENCY_UTIL, tsk);
|
||||
max_util = max(max_util, cpu_util);
|
||||
}
|
||||
|
||||
return energy;
|
||||
return em_pd_energy(pd->em_pd, max_util, sum_util);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -6386,21 +6346,19 @@ compute_energy(struct task_struct *p, int dst_cpu, struct perf_domain *pd)
|
|||
* other use-cases too. So, until someone finds a better way to solve this,
|
||||
* let's keep things simple by re-using the existing slow path.
|
||||
*/
|
||||
|
||||
static int find_energy_efficient_cpu(struct task_struct *p, int prev_cpu)
|
||||
{
|
||||
unsigned long prev_energy = ULONG_MAX, best_energy = ULONG_MAX;
|
||||
unsigned long prev_delta = ULONG_MAX, best_delta = ULONG_MAX;
|
||||
struct root_domain *rd = cpu_rq(smp_processor_id())->rd;
|
||||
unsigned long cpu_cap, util, base_energy = 0;
|
||||
int cpu, best_energy_cpu = prev_cpu;
|
||||
struct perf_domain *head, *pd;
|
||||
unsigned long cpu_cap, util;
|
||||
struct sched_domain *sd;
|
||||
struct perf_domain *pd;
|
||||
|
||||
rcu_read_lock();
|
||||
pd = rcu_dereference(rd->pd);
|
||||
if (!pd || READ_ONCE(rd->overutilized))
|
||||
goto fail;
|
||||
head = pd;
|
||||
|
||||
/*
|
||||
* Energy-aware wake-up happens on the lowest sched_domain starting
|
||||
|
|
@ -6417,9 +6375,14 @@ static int find_energy_efficient_cpu(struct task_struct *p, int prev_cpu)
|
|||
goto unlock;
|
||||
|
||||
for (; pd; pd = pd->next) {
|
||||
unsigned long cur_energy, spare_cap, max_spare_cap = 0;
|
||||
unsigned long cur_delta, spare_cap, max_spare_cap = 0;
|
||||
unsigned long base_energy_pd;
|
||||
int max_spare_cap_cpu = -1;
|
||||
|
||||
/* Compute the 'base' energy of the pd, without @p */
|
||||
base_energy_pd = compute_energy(p, -1, pd);
|
||||
base_energy += base_energy_pd;
|
||||
|
||||
for_each_cpu_and(cpu, perf_domain_span(pd), sched_domain_span(sd)) {
|
||||
if (!cpumask_test_cpu(cpu, p->cpus_ptr))
|
||||
continue;
|
||||
|
|
@ -6427,14 +6390,14 @@ static int find_energy_efficient_cpu(struct task_struct *p, int prev_cpu)
|
|||
/* Skip CPUs that will be overutilized. */
|
||||
util = cpu_util_next(cpu, p, cpu);
|
||||
cpu_cap = capacity_of(cpu);
|
||||
if (cpu_cap * 1024 < util * capacity_margin)
|
||||
if (!fits_capacity(util, cpu_cap))
|
||||
continue;
|
||||
|
||||
/* Always use prev_cpu as a candidate. */
|
||||
if (cpu == prev_cpu) {
|
||||
prev_energy = compute_energy(p, prev_cpu, head);
|
||||
best_energy = min(best_energy, prev_energy);
|
||||
continue;
|
||||
prev_delta = compute_energy(p, prev_cpu, pd);
|
||||
prev_delta -= base_energy_pd;
|
||||
best_delta = min(best_delta, prev_delta);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -6450,9 +6413,10 @@ static int find_energy_efficient_cpu(struct task_struct *p, int prev_cpu)
|
|||
|
||||
/* Evaluate the energy impact of using this CPU. */
|
||||
if (max_spare_cap_cpu >= 0) {
|
||||
cur_energy = compute_energy(p, max_spare_cap_cpu, head);
|
||||
if (cur_energy < best_energy) {
|
||||
best_energy = cur_energy;
|
||||
cur_delta = compute_energy(p, max_spare_cap_cpu, pd);
|
||||
cur_delta -= base_energy_pd;
|
||||
if (cur_delta < best_delta) {
|
||||
best_delta = cur_delta;
|
||||
best_energy_cpu = max_spare_cap_cpu;
|
||||
}
|
||||
}
|
||||
|
|
@ -6464,10 +6428,10 @@ unlock:
|
|||
* Pick the best CPU if prev_cpu cannot be used, or if it saves at
|
||||
* least 6% of the energy used by prev_cpu.
|
||||
*/
|
||||
if (prev_energy == ULONG_MAX)
|
||||
if (prev_delta == ULONG_MAX)
|
||||
return best_energy_cpu;
|
||||
|
||||
if ((prev_energy - best_energy) > (prev_energy >> 4))
|
||||
if ((prev_delta - best_delta) > ((prev_delta + base_energy) >> 4))
|
||||
return best_energy_cpu;
|
||||
|
||||
return prev_cpu;
|
||||
|
|
@ -6801,7 +6765,7 @@ again:
|
|||
goto idle;
|
||||
|
||||
#ifdef CONFIG_FAIR_GROUP_SCHED
|
||||
if (prev->sched_class != &fair_sched_class)
|
||||
if (!prev || prev->sched_class != &fair_sched_class)
|
||||
goto simple;
|
||||
|
||||
/*
|
||||
|
|
@ -6878,8 +6842,8 @@ again:
|
|||
goto done;
|
||||
simple:
|
||||
#endif
|
||||
|
||||
put_prev_task(rq, prev);
|
||||
if (prev)
|
||||
put_prev_task(rq, prev);
|
||||
|
||||
do {
|
||||
se = pick_next_entity(cfs_rq, NULL);
|
||||
|
|
@ -6907,11 +6871,13 @@ done: __maybe_unused;
|
|||
return p;
|
||||
|
||||
idle:
|
||||
update_misfit_status(NULL, rq);
|
||||
new_tasks = idle_balance(rq, rf);
|
||||
if (!rf)
|
||||
return NULL;
|
||||
|
||||
new_tasks = newidle_balance(rq, rf);
|
||||
|
||||
/*
|
||||
* Because idle_balance() releases (and re-acquires) rq->lock, it is
|
||||
* Because newidle_balance() releases (and re-acquires) rq->lock, it is
|
||||
* possible for any higher priority task to appear. In that case we
|
||||
* must re-start the pick_next_entity() loop.
|
||||
*/
|
||||
|
|
@ -6933,7 +6899,7 @@ idle:
|
|||
/*
|
||||
* Account for a descheduled task:
|
||||
*/
|
||||
static void put_prev_task_fair(struct rq *rq, struct task_struct *prev)
|
||||
static void put_prev_task_fair(struct rq *rq, struct task_struct *prev, struct rq_flags *rf)
|
||||
{
|
||||
struct sched_entity *se = &prev->se;
|
||||
struct cfs_rq *cfs_rq;
|
||||
|
|
@ -7435,7 +7401,7 @@ static int detach_tasks(struct lb_env *env)
|
|||
detached++;
|
||||
env->imbalance -= load;
|
||||
|
||||
#ifdef CONFIG_PREEMPT
|
||||
#ifdef CONFIG_PREEMPTION
|
||||
/*
|
||||
* NEWIDLE balancing is a source of latency, so preemptible
|
||||
* kernels will stop after the first task is detached to minimize
|
||||
|
|
@ -7982,8 +7948,7 @@ group_is_overloaded(struct lb_env *env, struct sg_lb_stats *sgs)
|
|||
static inline bool
|
||||
group_smaller_min_cpu_capacity(struct sched_group *sg, struct sched_group *ref)
|
||||
{
|
||||
return sg->sgc->min_capacity * capacity_margin <
|
||||
ref->sgc->min_capacity * 1024;
|
||||
return fits_capacity(sg->sgc->min_capacity, ref->sgc->min_capacity);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -7993,8 +7958,7 @@ group_smaller_min_cpu_capacity(struct sched_group *sg, struct sched_group *ref)
|
|||
static inline bool
|
||||
group_smaller_max_cpu_capacity(struct sched_group *sg, struct sched_group *ref)
|
||||
{
|
||||
return sg->sgc->max_capacity * capacity_margin <
|
||||
ref->sgc->max_capacity * 1024;
|
||||
return fits_capacity(sg->sgc->max_capacity, ref->sgc->max_capacity);
|
||||
}
|
||||
|
||||
static inline enum
|
||||
|
|
@ -9052,9 +9016,10 @@ more_balance:
|
|||
out_balanced:
|
||||
/*
|
||||
* We reach balance although we may have faced some affinity
|
||||
* constraints. Clear the imbalance flag if it was set.
|
||||
* constraints. Clear the imbalance flag only if other tasks got
|
||||
* a chance to move and fix the imbalance.
|
||||
*/
|
||||
if (sd_parent) {
|
||||
if (sd_parent && !(env.flags & LBF_ALL_PINNED)) {
|
||||
int *group_imbalance = &sd_parent->groups->sgc->imbalance;
|
||||
|
||||
if (*group_imbalance)
|
||||
|
|
@ -9075,10 +9040,10 @@ out_one_pinned:
|
|||
ld_moved = 0;
|
||||
|
||||
/*
|
||||
* idle_balance() disregards balance intervals, so we could repeatedly
|
||||
* reach this code, which would lead to balance_interval skyrocketting
|
||||
* in a short amount of time. Skip the balance_interval increase logic
|
||||
* to avoid that.
|
||||
* newidle_balance() disregards balance intervals, so we could
|
||||
* repeatedly reach this code, which would lead to balance_interval
|
||||
* skyrocketting in a short amount of time. Skip the balance_interval
|
||||
* increase logic to avoid that.
|
||||
*/
|
||||
if (env.idle == CPU_NEWLY_IDLE)
|
||||
goto out;
|
||||
|
|
@ -9788,7 +9753,7 @@ static inline void nohz_newidle_balance(struct rq *this_rq) { }
|
|||
* idle_balance is called by schedule() if this_cpu is about to become
|
||||
* idle. Attempts to pull tasks from other CPUs.
|
||||
*/
|
||||
static int idle_balance(struct rq *this_rq, struct rq_flags *rf)
|
||||
int newidle_balance(struct rq *this_rq, struct rq_flags *rf)
|
||||
{
|
||||
unsigned long next_balance = jiffies + HZ;
|
||||
int this_cpu = this_rq->cpu;
|
||||
|
|
@ -9796,6 +9761,7 @@ static int idle_balance(struct rq *this_rq, struct rq_flags *rf)
|
|||
int pulled_task = 0;
|
||||
u64 curr_cost = 0;
|
||||
|
||||
update_misfit_status(NULL, this_rq);
|
||||
/*
|
||||
* We must set idle_stamp _before_ calling idle_balance(), such that we
|
||||
* measure the duration of idle_balance() as idle time.
|
||||
|
|
@ -10180,9 +10146,19 @@ static void switched_to_fair(struct rq *rq, struct task_struct *p)
|
|||
* This routine is mostly called to set cfs_rq->curr field when a task
|
||||
* migrates between groups/classes.
|
||||
*/
|
||||
static void set_curr_task_fair(struct rq *rq)
|
||||
static void set_next_task_fair(struct rq *rq, struct task_struct *p)
|
||||
{
|
||||
struct sched_entity *se = &rq->curr->se;
|
||||
struct sched_entity *se = &p->se;
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
if (task_on_rq_queued(p)) {
|
||||
/*
|
||||
* Move the next running task to the front of the list, so our
|
||||
* cfs_tasks list becomes MRU one.
|
||||
*/
|
||||
list_move(&se->group_node, &rq->cfs_tasks);
|
||||
}
|
||||
#endif
|
||||
|
||||
for_each_sched_entity(se) {
|
||||
struct cfs_rq *cfs_rq = cfs_rq_of(se);
|
||||
|
|
@ -10300,18 +10276,18 @@ err:
|
|||
void online_fair_sched_group(struct task_group *tg)
|
||||
{
|
||||
struct sched_entity *se;
|
||||
struct rq_flags rf;
|
||||
struct rq *rq;
|
||||
int i;
|
||||
|
||||
for_each_possible_cpu(i) {
|
||||
rq = cpu_rq(i);
|
||||
se = tg->se[i];
|
||||
|
||||
raw_spin_lock_irq(&rq->lock);
|
||||
rq_lock_irq(rq, &rf);
|
||||
update_rq_clock(rq);
|
||||
attach_entity_cfs_rq(se);
|
||||
sync_throttle(tg, i);
|
||||
raw_spin_unlock_irq(&rq->lock);
|
||||
rq_unlock_irq(rq, &rf);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -10453,7 +10429,9 @@ const struct sched_class fair_sched_class = {
|
|||
.check_preempt_curr = check_preempt_wakeup,
|
||||
|
||||
.pick_next_task = pick_next_task_fair,
|
||||
|
||||
.put_prev_task = put_prev_task_fair,
|
||||
.set_next_task = set_next_task_fair,
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
.select_task_rq = select_task_rq_fair,
|
||||
|
|
@ -10466,7 +10444,6 @@ const struct sched_class fair_sched_class = {
|
|||
.set_cpus_allowed = set_cpus_allowed_common,
|
||||
#endif
|
||||
|
||||
.set_curr_task = set_curr_task_fair,
|
||||
.task_tick = task_tick_fair,
|
||||
.task_fork = task_fork_fair,
|
||||
|
||||
|
|
|
|||
|
|
@ -241,13 +241,14 @@ static void do_idle(void)
|
|||
check_pgt_cache();
|
||||
rmb();
|
||||
|
||||
local_irq_disable();
|
||||
|
||||
if (cpu_is_offline(cpu)) {
|
||||
tick_nohz_idle_stop_tick_protected();
|
||||
tick_nohz_idle_stop_tick();
|
||||
cpuhp_report_idle_dead();
|
||||
arch_cpu_idle_dead();
|
||||
}
|
||||
|
||||
local_irq_disable();
|
||||
arch_cpu_idle_enter();
|
||||
|
||||
/*
|
||||
|
|
@ -311,7 +312,7 @@ static enum hrtimer_restart idle_inject_timer_fn(struct hrtimer *timer)
|
|||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
void play_idle(unsigned long duration_ms)
|
||||
void play_idle(unsigned long duration_us)
|
||||
{
|
||||
struct idle_timer it;
|
||||
|
||||
|
|
@ -323,7 +324,7 @@ void play_idle(unsigned long duration_ms)
|
|||
WARN_ON_ONCE(current->nr_cpus_allowed != 1);
|
||||
WARN_ON_ONCE(!(current->flags & PF_KTHREAD));
|
||||
WARN_ON_ONCE(!(current->flags & PF_NO_SETAFFINITY));
|
||||
WARN_ON_ONCE(!duration_ms);
|
||||
WARN_ON_ONCE(!duration_us);
|
||||
|
||||
rcu_sleep_check();
|
||||
preempt_disable();
|
||||
|
|
@ -333,7 +334,8 @@ void play_idle(unsigned long duration_ms)
|
|||
it.done = 0;
|
||||
hrtimer_init_on_stack(&it.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
it.timer.function = idle_inject_timer_fn;
|
||||
hrtimer_start(&it.timer, ms_to_ktime(duration_ms), HRTIMER_MODE_REL_PINNED);
|
||||
hrtimer_start(&it.timer, ns_to_ktime(duration_us * NSEC_PER_USEC),
|
||||
HRTIMER_MODE_REL_PINNED);
|
||||
|
||||
while (!READ_ONCE(it.done))
|
||||
do_idle();
|
||||
|
|
@ -374,14 +376,27 @@ static void check_preempt_curr_idle(struct rq *rq, struct task_struct *p, int fl
|
|||
resched_curr(rq);
|
||||
}
|
||||
|
||||
static void put_prev_task_idle(struct rq *rq, struct task_struct *prev, struct rq_flags *rf)
|
||||
{
|
||||
}
|
||||
|
||||
static void set_next_task_idle(struct rq *rq, struct task_struct *next)
|
||||
{
|
||||
update_idle_core(rq);
|
||||
schedstat_inc(rq->sched_goidle);
|
||||
}
|
||||
|
||||
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);
|
||||
struct task_struct *next = rq->idle;
|
||||
|
||||
return rq->idle;
|
||||
if (prev)
|
||||
put_prev_task(rq, prev);
|
||||
|
||||
set_next_task_idle(rq, next);
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -397,10 +412,6 @@ dequeue_task_idle(struct rq *rq, struct task_struct *p, int flags)
|
|||
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.
|
||||
*
|
||||
|
|
@ -413,10 +424,6 @@ 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();
|
||||
|
|
@ -451,13 +458,13 @@ const struct sched_class idle_sched_class = {
|
|||
|
||||
.pick_next_task = pick_next_task_idle,
|
||||
.put_prev_task = put_prev_task_idle,
|
||||
.set_next_task = set_next_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,
|
||||
|
|
|
|||
|
|
@ -22,9 +22,17 @@ EXPORT_SYMBOL_GPL(housekeeping_enabled);
|
|||
|
||||
int housekeeping_any_cpu(enum hk_flags flags)
|
||||
{
|
||||
if (static_branch_unlikely(&housekeeping_overridden))
|
||||
if (housekeeping_flags & flags)
|
||||
int cpu;
|
||||
|
||||
if (static_branch_unlikely(&housekeeping_overridden)) {
|
||||
if (housekeeping_flags & flags) {
|
||||
cpu = sched_numa_find_closest(housekeeping_mask, smp_processor_id());
|
||||
if (cpu < nr_cpu_ids)
|
||||
return cpu;
|
||||
|
||||
return cpumask_any_and(housekeeping_mask, cpu_online_mask);
|
||||
}
|
||||
}
|
||||
return smp_processor_id();
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(housekeeping_any_cpu);
|
||||
|
|
|
|||
|
|
@ -1198,7 +1198,7 @@ static ssize_t psi_write(struct file *file, const char __user *user_buf,
|
|||
if (static_branch_likely(&psi_disabled))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
buf_size = min(nbytes, (sizeof(buf) - 1));
|
||||
buf_size = min(nbytes, sizeof(buf));
|
||||
if (copy_from_user(buf, user_buf, buf_size))
|
||||
return -EFAULT;
|
||||
|
||||
|
|
|
|||
|
|
@ -45,8 +45,8 @@ void init_rt_bandwidth(struct rt_bandwidth *rt_b, u64 period, u64 runtime)
|
|||
|
||||
raw_spin_lock_init(&rt_b->rt_runtime_lock);
|
||||
|
||||
hrtimer_init(&rt_b->rt_period_timer,
|
||||
CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
hrtimer_init(&rt_b->rt_period_timer, CLOCK_MONOTONIC,
|
||||
HRTIMER_MODE_REL_HARD);
|
||||
rt_b->rt_period_timer.function = sched_rt_period_timer;
|
||||
}
|
||||
|
||||
|
|
@ -67,7 +67,8 @@ static void start_rt_bandwidth(struct rt_bandwidth *rt_b)
|
|||
* to update the period.
|
||||
*/
|
||||
hrtimer_forward_now(&rt_b->rt_period_timer, ns_to_ktime(0));
|
||||
hrtimer_start_expires(&rt_b->rt_period_timer, HRTIMER_MODE_ABS_PINNED);
|
||||
hrtimer_start_expires(&rt_b->rt_period_timer,
|
||||
HRTIMER_MODE_ABS_PINNED_HARD);
|
||||
}
|
||||
raw_spin_unlock(&rt_b->rt_runtime_lock);
|
||||
}
|
||||
|
|
@ -1498,12 +1499,22 @@ static void check_preempt_curr_rt(struct rq *rq, struct task_struct *p, int flag
|
|||
#endif
|
||||
}
|
||||
|
||||
static inline void set_next_task(struct rq *rq, struct task_struct *p)
|
||||
static inline void set_next_task_rt(struct rq *rq, struct task_struct *p)
|
||||
{
|
||||
p->se.exec_start = rq_clock_task(rq);
|
||||
|
||||
/* The running task is never eligible for pushing */
|
||||
dequeue_pushable_task(rq, p);
|
||||
|
||||
/*
|
||||
* If prev task was rt, put_prev_task() has already updated the
|
||||
* utilization. We only care of the case where we start to schedule a
|
||||
* rt task
|
||||
*/
|
||||
if (rq->curr->sched_class != &rt_sched_class)
|
||||
update_rt_rq_load_avg(rq_clock_pelt(rq), rq, 0);
|
||||
|
||||
rt_queue_push_tasks(rq);
|
||||
}
|
||||
|
||||
static struct sched_rt_entity *pick_next_rt_entity(struct rq *rq,
|
||||
|
|
@ -1543,56 +1554,19 @@ pick_next_task_rt(struct rq *rq, struct task_struct *prev, struct rq_flags *rf)
|
|||
struct task_struct *p;
|
||||
struct rt_rq *rt_rq = &rq->rt;
|
||||
|
||||
if (need_pull_rt_task(rq, prev)) {
|
||||
/*
|
||||
* This is OK, because current is on_cpu, which avoids it being
|
||||
* picked for load-balance and preemption/IRQs are still
|
||||
* disabled avoiding further scheduler activity on it and we're
|
||||
* being very careful to re-start the picking loop.
|
||||
*/
|
||||
rq_unpin_lock(rq, rf);
|
||||
pull_rt_task(rq);
|
||||
rq_repin_lock(rq, rf);
|
||||
/*
|
||||
* pull_rt_task() can drop (and re-acquire) rq->lock; this
|
||||
* means a dl or stop task can slip in, in which case we need
|
||||
* to re-start task selection.
|
||||
*/
|
||||
if (unlikely((rq->stop && task_on_rq_queued(rq->stop)) ||
|
||||
rq->dl.dl_nr_running))
|
||||
return RETRY_TASK;
|
||||
}
|
||||
|
||||
/*
|
||||
* We may dequeue prev's rt_rq in put_prev_task().
|
||||
* So, we update time before rt_queued check.
|
||||
*/
|
||||
if (prev->sched_class == &rt_sched_class)
|
||||
update_curr_rt(rq);
|
||||
WARN_ON_ONCE(prev || rf);
|
||||
|
||||
if (!rt_rq->rt_queued)
|
||||
return NULL;
|
||||
|
||||
put_prev_task(rq, prev);
|
||||
|
||||
p = _pick_next_task_rt(rq);
|
||||
|
||||
set_next_task(rq, p);
|
||||
|
||||
rt_queue_push_tasks(rq);
|
||||
|
||||
/*
|
||||
* If prev task was rt, put_prev_task() has already updated the
|
||||
* utilization. We only care of the case where we start to schedule a
|
||||
* rt task
|
||||
*/
|
||||
if (rq->curr->sched_class != &rt_sched_class)
|
||||
update_rt_rq_load_avg(rq_clock_pelt(rq), rq, 0);
|
||||
set_next_task_rt(rq, p);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
static void put_prev_task_rt(struct rq *rq, struct task_struct *p)
|
||||
static void put_prev_task_rt(struct rq *rq, struct task_struct *p, struct rq_flags *rf)
|
||||
{
|
||||
update_curr_rt(rq);
|
||||
|
||||
|
|
@ -1604,6 +1578,18 @@ static void put_prev_task_rt(struct rq *rq, struct task_struct *p)
|
|||
*/
|
||||
if (on_rt_rq(&p->rt) && p->nr_cpus_allowed > 1)
|
||||
enqueue_pushable_task(rq, p);
|
||||
|
||||
if (rf && !on_rt_rq(&p->rt) && need_pull_rt_task(rq, p)) {
|
||||
/*
|
||||
* This is OK, because current is on_cpu, which avoids it being
|
||||
* picked for load-balance and preemption/IRQs are still
|
||||
* disabled avoiding further scheduler activity on it and we've
|
||||
* not yet started the picking loop.
|
||||
*/
|
||||
rq_unpin_lock(rq, rf);
|
||||
pull_rt_task(rq);
|
||||
rq_repin_lock(rq, rf);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
|
|
@ -2304,8 +2290,10 @@ static void watchdog(struct rq *rq, struct task_struct *p)
|
|||
}
|
||||
|
||||
next = DIV_ROUND_UP(min(soft, hard), USEC_PER_SEC/HZ);
|
||||
if (p->rt.timeout > next)
|
||||
p->cputime_expires.sched_exp = p->se.sum_exec_runtime;
|
||||
if (p->rt.timeout > next) {
|
||||
posix_cputimers_rt_watchdog(&p->posix_cputimers,
|
||||
p->se.sum_exec_runtime);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
|
@ -2354,11 +2342,6 @@ static void task_tick_rt(struct rq *rq, struct task_struct *p, int queued)
|
|||
}
|
||||
}
|
||||
|
||||
static void set_curr_task_rt(struct rq *rq)
|
||||
{
|
||||
set_next_task(rq, rq->curr);
|
||||
}
|
||||
|
||||
static unsigned int get_rr_interval_rt(struct rq *rq, struct task_struct *task)
|
||||
{
|
||||
/*
|
||||
|
|
@ -2380,6 +2363,7 @@ const struct sched_class rt_sched_class = {
|
|||
|
||||
.pick_next_task = pick_next_task_rt,
|
||||
.put_prev_task = put_prev_task_rt,
|
||||
.set_next_task = set_next_task_rt,
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
.select_task_rq = select_task_rq_rt,
|
||||
|
|
@ -2391,7 +2375,6 @@ const struct sched_class rt_sched_class = {
|
|||
.switched_from = switched_from_rt,
|
||||
#endif
|
||||
|
||||
.set_curr_task = set_curr_task_rt,
|
||||
.task_tick = task_tick_rt,
|
||||
|
||||
.get_rr_interval = get_rr_interval_rt,
|
||||
|
|
|
|||
|
|
@ -335,8 +335,6 @@ struct cfs_bandwidth {
|
|||
u64 quota;
|
||||
u64 runtime;
|
||||
s64 hierarchical_quota;
|
||||
u64 runtime_expires;
|
||||
int expires_seq;
|
||||
|
||||
u8 idle;
|
||||
u8 period_active;
|
||||
|
|
@ -393,6 +391,16 @@ struct task_group {
|
|||
#endif
|
||||
|
||||
struct cfs_bandwidth cfs_bandwidth;
|
||||
|
||||
#ifdef CONFIG_UCLAMP_TASK_GROUP
|
||||
/* The two decimal precision [%] value requested from user-space */
|
||||
unsigned int uclamp_pct[UCLAMP_CNT];
|
||||
/* Clamp values requested for a task group */
|
||||
struct uclamp_se uclamp_req[UCLAMP_CNT];
|
||||
/* Effective clamp values used for a task group */
|
||||
struct uclamp_se uclamp[UCLAMP_CNT];
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
#ifdef CONFIG_FAIR_GROUP_SCHED
|
||||
|
|
@ -483,7 +491,8 @@ struct cfs_rq {
|
|||
struct load_weight load;
|
||||
unsigned long runnable_weight;
|
||||
unsigned int nr_running;
|
||||
unsigned int h_nr_running;
|
||||
unsigned int h_nr_running; /* SCHED_{NORMAL,BATCH,IDLE} */
|
||||
unsigned int idle_h_nr_running; /* SCHED_IDLE */
|
||||
|
||||
u64 exec_clock;
|
||||
u64 min_vruntime;
|
||||
|
|
@ -556,8 +565,6 @@ struct cfs_rq {
|
|||
|
||||
#ifdef CONFIG_CFS_BANDWIDTH
|
||||
int runtime_enabled;
|
||||
int expires_seq;
|
||||
u64 runtime_expires;
|
||||
s64 runtime_remaining;
|
||||
|
||||
u64 throttled_clock;
|
||||
|
|
@ -777,9 +784,6 @@ struct root_domain {
|
|||
struct perf_domain __rcu *pd;
|
||||
};
|
||||
|
||||
extern struct root_domain def_root_domain;
|
||||
extern struct mutex sched_domains_mutex;
|
||||
|
||||
extern void init_defrootdomain(void);
|
||||
extern int sched_init_domains(const struct cpumask *cpu_map);
|
||||
extern void rq_attach_root(struct rq *rq, struct root_domain *rd);
|
||||
|
|
@ -1261,16 +1265,18 @@ enum numa_topology_type {
|
|||
extern enum numa_topology_type sched_numa_topology_type;
|
||||
extern int sched_max_numa_distance;
|
||||
extern bool find_numa_distance(int distance);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_NUMA
|
||||
extern void sched_init_numa(void);
|
||||
extern void sched_domains_numa_masks_set(unsigned int cpu);
|
||||
extern void sched_domains_numa_masks_clear(unsigned int cpu);
|
||||
extern int sched_numa_find_closest(const struct cpumask *cpus, int cpu);
|
||||
#else
|
||||
static inline void sched_init_numa(void) { }
|
||||
static inline void sched_domains_numa_masks_set(unsigned int cpu) { }
|
||||
static inline void sched_domains_numa_masks_clear(unsigned int cpu) { }
|
||||
static inline int sched_numa_find_closest(const struct cpumask *cpus, int cpu)
|
||||
{
|
||||
return nr_cpu_ids;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_NUMA_BALANCING
|
||||
|
|
@ -1449,10 +1455,14 @@ static inline void unregister_sched_domain_sysctl(void)
|
|||
}
|
||||
#endif
|
||||
|
||||
extern int newidle_balance(struct rq *this_rq, struct rq_flags *rf);
|
||||
|
||||
#else
|
||||
|
||||
static inline void sched_ttwu_pending(void) { }
|
||||
|
||||
static inline int newidle_balance(struct rq *this_rq, struct rq_flags *rf) { return 0; }
|
||||
|
||||
#endif /* CONFIG_SMP */
|
||||
|
||||
#include "stats.h"
|
||||
|
|
@ -1700,17 +1710,21 @@ struct sched_class {
|
|||
void (*check_preempt_curr)(struct rq *rq, struct task_struct *p, int flags);
|
||||
|
||||
/*
|
||||
* It is the responsibility of the pick_next_task() method that will
|
||||
* return the next task to call put_prev_task() on the @prev task or
|
||||
* something equivalent.
|
||||
* Both @prev and @rf are optional and may be NULL, in which case the
|
||||
* caller must already have invoked put_prev_task(rq, prev, rf).
|
||||
*
|
||||
* May return RETRY_TASK when it finds a higher prio class has runnable
|
||||
* tasks.
|
||||
* Otherwise it is the responsibility of the pick_next_task() to call
|
||||
* put_prev_task() on the @prev task or something equivalent, IFF it
|
||||
* returns a next task.
|
||||
*
|
||||
* In that case (@rf != NULL) it may return RETRY_TASK when it finds a
|
||||
* higher prio class has runnable tasks.
|
||||
*/
|
||||
struct task_struct * (*pick_next_task)(struct rq *rq,
|
||||
struct task_struct *prev,
|
||||
struct rq_flags *rf);
|
||||
void (*put_prev_task)(struct rq *rq, struct task_struct *p);
|
||||
void (*put_prev_task)(struct rq *rq, struct task_struct *p, struct rq_flags *rf);
|
||||
void (*set_next_task)(struct rq *rq, struct task_struct *p);
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
int (*select_task_rq)(struct task_struct *p, int task_cpu, int sd_flag, int flags);
|
||||
|
|
@ -1725,7 +1739,6 @@ struct sched_class {
|
|||
void (*rq_offline)(struct rq *rq);
|
||||
#endif
|
||||
|
||||
void (*set_curr_task)(struct rq *rq);
|
||||
void (*task_tick)(struct rq *rq, struct task_struct *p, int queued);
|
||||
void (*task_fork)(struct task_struct *p);
|
||||
void (*task_dead)(struct task_struct *p);
|
||||
|
|
@ -1755,12 +1768,14 @@ struct sched_class {
|
|||
|
||||
static inline void put_prev_task(struct rq *rq, struct task_struct *prev)
|
||||
{
|
||||
prev->sched_class->put_prev_task(rq, prev);
|
||||
WARN_ON_ONCE(rq->curr != prev);
|
||||
prev->sched_class->put_prev_task(rq, prev, NULL);
|
||||
}
|
||||
|
||||
static inline void set_curr_task(struct rq *rq, struct task_struct *curr)
|
||||
static inline void set_next_task(struct rq *rq, struct task_struct *next)
|
||||
{
|
||||
curr->sched_class->set_curr_task(rq);
|
||||
WARN_ON_ONCE(rq->curr != next);
|
||||
next->sched_class->set_next_task(rq, next);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
|
|
@ -1943,7 +1958,7 @@ unsigned long arch_scale_freq_capacity(int cpu)
|
|||
#endif
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
#ifdef CONFIG_PREEMPT
|
||||
#ifdef CONFIG_PREEMPTION
|
||||
|
||||
static inline void double_rq_lock(struct rq *rq1, struct rq *rq2);
|
||||
|
||||
|
|
@ -1995,7 +2010,7 @@ static inline int _double_lock_balance(struct rq *this_rq, struct rq *busiest)
|
|||
return ret;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_PREEMPT */
|
||||
#endif /* CONFIG_PREEMPTION */
|
||||
|
||||
/*
|
||||
* double_lock_balance - lock the busiest runqueue, this_rq is locked already.
|
||||
|
|
@ -2266,7 +2281,7 @@ static inline void cpufreq_update_util(struct rq *rq, unsigned int flags) {}
|
|||
#endif /* CONFIG_CPU_FREQ */
|
||||
|
||||
#ifdef CONFIG_UCLAMP_TASK
|
||||
unsigned int uclamp_eff_value(struct task_struct *p, unsigned int clamp_id);
|
||||
enum uclamp_id uclamp_eff_value(struct task_struct *p, enum uclamp_id clamp_id);
|
||||
|
||||
static __always_inline
|
||||
unsigned int uclamp_util_with(struct rq *rq, unsigned int util,
|
||||
|
|
|
|||
|
|
@ -157,9 +157,10 @@ static inline void sched_info_dequeued(struct rq *rq, struct task_struct *t)
|
|||
{
|
||||
unsigned long long now = rq_clock(rq), delta = 0;
|
||||
|
||||
if (unlikely(sched_info_on()))
|
||||
if (sched_info_on()) {
|
||||
if (t->sched_info.last_queued)
|
||||
delta = now - t->sched_info.last_queued;
|
||||
}
|
||||
sched_info_reset_dequeued(t);
|
||||
t->sched_info.run_delay += delta;
|
||||
|
||||
|
|
@ -192,7 +193,7 @@ 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 (sched_info_on()) {
|
||||
if (!t->sched_info.last_queued)
|
||||
t->sched_info.last_queued = rq_clock(rq);
|
||||
}
|
||||
|
|
@ -239,7 +240,7 @@ __sched_info_switch(struct rq *rq, struct task_struct *prev, struct task_struct
|
|||
static inline void
|
||||
sched_info_switch(struct rq *rq, struct task_struct *prev, struct task_struct *next)
|
||||
{
|
||||
if (unlikely(sched_info_on()))
|
||||
if (sched_info_on())
|
||||
__sched_info_switch(rq, prev, next);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,17 +23,22 @@ check_preempt_curr_stop(struct rq *rq, struct task_struct *p, int flags)
|
|||
/* we're never preempted */
|
||||
}
|
||||
|
||||
static void set_next_task_stop(struct rq *rq, struct task_struct *stop)
|
||||
{
|
||||
stop->se.exec_start = rq_clock_task(rq);
|
||||
}
|
||||
|
||||
static struct task_struct *
|
||||
pick_next_task_stop(struct rq *rq, struct task_struct *prev, struct rq_flags *rf)
|
||||
{
|
||||
struct task_struct *stop = rq->stop;
|
||||
|
||||
WARN_ON_ONCE(prev || rf);
|
||||
|
||||
if (!stop || !task_on_rq_queued(stop))
|
||||
return NULL;
|
||||
|
||||
put_prev_task(rq, prev);
|
||||
|
||||
stop->se.exec_start = rq_clock_task(rq);
|
||||
set_next_task_stop(rq, stop);
|
||||
|
||||
return stop;
|
||||
}
|
||||
|
|
@ -55,7 +60,7 @@ static void yield_task_stop(struct rq *rq)
|
|||
BUG(); /* the stop task should never yield, its pointless. */
|
||||
}
|
||||
|
||||
static void put_prev_task_stop(struct rq *rq, struct task_struct *prev)
|
||||
static void put_prev_task_stop(struct rq *rq, struct task_struct *prev, struct rq_flags *rf)
|
||||
{
|
||||
struct task_struct *curr = rq->curr;
|
||||
u64 delta_exec;
|
||||
|
|
@ -86,13 +91,6 @@ static void task_tick_stop(struct rq *rq, struct task_struct *curr, int queued)
|
|||
{
|
||||
}
|
||||
|
||||
static void set_curr_task_stop(struct rq *rq)
|
||||
{
|
||||
struct task_struct *stop = rq->stop;
|
||||
|
||||
stop->se.exec_start = rq_clock_task(rq);
|
||||
}
|
||||
|
||||
static void switched_to_stop(struct rq *rq, struct task_struct *p)
|
||||
{
|
||||
BUG(); /* its impossible to change to this class */
|
||||
|
|
@ -128,13 +126,13 @@ const struct sched_class stop_sched_class = {
|
|||
|
||||
.pick_next_task = pick_next_task_stop,
|
||||
.put_prev_task = put_prev_task_stop,
|
||||
.set_next_task = set_next_task_stop,
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
.select_task_rq = select_task_rq_stop,
|
||||
.set_cpus_allowed = set_cpus_allowed_common,
|
||||
#endif
|
||||
|
||||
.set_curr_task = set_curr_task_stop,
|
||||
.task_tick = task_tick_stop,
|
||||
|
||||
.get_rr_interval = get_rr_interval_stop,
|
||||
|
|
|
|||
|
|
@ -1284,6 +1284,7 @@ static int sched_domains_curr_level;
|
|||
int sched_max_numa_distance;
|
||||
static int *sched_domains_numa_distance;
|
||||
static struct cpumask ***sched_domains_numa_masks;
|
||||
int __read_mostly node_reclaim_distance = RECLAIM_DISTANCE;
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
|
@ -1402,7 +1403,7 @@ sd_init(struct sched_domain_topology_level *tl,
|
|||
|
||||
sd->flags &= ~SD_PREFER_SIBLING;
|
||||
sd->flags |= SD_SERIALIZE;
|
||||
if (sched_domains_numa_distance[tl->numa_level] > RECLAIM_DISTANCE) {
|
||||
if (sched_domains_numa_distance[tl->numa_level] > node_reclaim_distance) {
|
||||
sd->flags &= ~(SD_BALANCE_EXEC |
|
||||
SD_BALANCE_FORK |
|
||||
SD_WAKE_AFFINE);
|
||||
|
|
@ -1724,6 +1725,26 @@ void sched_domains_numa_masks_clear(unsigned int cpu)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* sched_numa_find_closest() - given the NUMA topology, find the cpu
|
||||
* closest to @cpu from @cpumask.
|
||||
* cpumask: cpumask to find a cpu from
|
||||
* cpu: cpu to be close to
|
||||
*
|
||||
* returns: cpu, or nr_cpu_ids when nothing found.
|
||||
*/
|
||||
int sched_numa_find_closest(const struct cpumask *cpus, int cpu)
|
||||
{
|
||||
int i, j = cpu_to_node(cpu);
|
||||
|
||||
for (i = 0; i < sched_domains_numa_levels; i++) {
|
||||
cpu = cpumask_any_and(cpus, sched_domains_numa_masks[i][j]);
|
||||
if (cpu < nr_cpu_ids)
|
||||
return cpu;
|
||||
}
|
||||
return nr_cpu_ids;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_NUMA */
|
||||
|
||||
static int __sdt_alloc(const struct cpumask *cpu_map)
|
||||
|
|
@ -2149,16 +2170,16 @@ static int dattrs_equal(struct sched_domain_attr *cur, int idx_cur,
|
|||
* ndoms_new == 0 is a special case for destroying existing domains,
|
||||
* and it will not create the default domain.
|
||||
*
|
||||
* Call with hotplug lock held
|
||||
* Call with hotplug lock and sched_domains_mutex held
|
||||
*/
|
||||
void partition_sched_domains(int ndoms_new, cpumask_var_t doms_new[],
|
||||
struct sched_domain_attr *dattr_new)
|
||||
void partition_sched_domains_locked(int ndoms_new, cpumask_var_t doms_new[],
|
||||
struct sched_domain_attr *dattr_new)
|
||||
{
|
||||
bool __maybe_unused has_eas = false;
|
||||
int i, j, n;
|
||||
int new_topology;
|
||||
|
||||
mutex_lock(&sched_domains_mutex);
|
||||
lockdep_assert_held(&sched_domains_mutex);
|
||||
|
||||
/* Always unregister in case we don't destroy any domains: */
|
||||
unregister_sched_domain_sysctl();
|
||||
|
|
@ -2183,8 +2204,19 @@ void partition_sched_domains(int ndoms_new, cpumask_var_t doms_new[],
|
|||
for (i = 0; i < ndoms_cur; i++) {
|
||||
for (j = 0; j < n && !new_topology; j++) {
|
||||
if (cpumask_equal(doms_cur[i], doms_new[j]) &&
|
||||
dattrs_equal(dattr_cur, i, dattr_new, j))
|
||||
dattrs_equal(dattr_cur, i, dattr_new, j)) {
|
||||
struct root_domain *rd;
|
||||
|
||||
/*
|
||||
* This domain won't be destroyed and as such
|
||||
* its dl_bw->total_bw needs to be cleared. It
|
||||
* will be recomputed in function
|
||||
* update_tasks_root_domain().
|
||||
*/
|
||||
rd = cpu_rq(cpumask_any(doms_cur[i]))->rd;
|
||||
dl_clear_root_domain(rd);
|
||||
goto match1;
|
||||
}
|
||||
}
|
||||
/* No match - a current sched domain not in new doms_new[] */
|
||||
detach_destroy_domains(doms_cur[i]);
|
||||
|
|
@ -2241,6 +2273,15 @@ match3:
|
|||
ndoms_cur = ndoms_new;
|
||||
|
||||
register_sched_domain_sysctl();
|
||||
}
|
||||
|
||||
/*
|
||||
* Call with hotplug lock held
|
||||
*/
|
||||
void partition_sched_domains(int ndoms_new, cpumask_var_t doms_new[],
|
||||
struct sched_domain_attr *dattr_new)
|
||||
{
|
||||
mutex_lock(&sched_domains_mutex);
|
||||
partition_sched_domains_locked(ndoms_new, doms_new, dattr_new);
|
||||
mutex_unlock(&sched_domains_mutex);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3678,8 +3678,11 @@ static int copy_siginfo_from_user_any(kernel_siginfo_t *kinfo, siginfo_t *info)
|
|||
|
||||
static struct pid *pidfd_to_pid(const struct file *file)
|
||||
{
|
||||
if (file->f_op == &pidfd_fops)
|
||||
return file->private_data;
|
||||
struct pid *pid;
|
||||
|
||||
pid = pidfd_pid(file);
|
||||
if (!IS_ERR(pid))
|
||||
return pid;
|
||||
|
||||
return tgid_pidfd_to_pid(file);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
* @nr_entries: Number of entries in the storage array
|
||||
* @spaces: Number of leading spaces to print
|
||||
*/
|
||||
void stack_trace_print(unsigned long *entries, unsigned int nr_entries,
|
||||
void stack_trace_print(const unsigned long *entries, unsigned int nr_entries,
|
||||
int spaces)
|
||||
{
|
||||
unsigned int i;
|
||||
|
|
@ -43,7 +43,7 @@ EXPORT_SYMBOL_GPL(stack_trace_print);
|
|||
*
|
||||
* Return: Number of bytes printed.
|
||||
*/
|
||||
int stack_trace_snprint(char *buf, size_t size, unsigned long *entries,
|
||||
int stack_trace_snprint(char *buf, size_t size, const unsigned long *entries,
|
||||
unsigned int nr_entries, int spaces)
|
||||
{
|
||||
unsigned int generated, i, total = 0;
|
||||
|
|
|
|||
|
|
@ -383,6 +383,7 @@ static bool queue_stop_cpus_work(const struct cpumask *cpumask,
|
|||
*/
|
||||
preempt_disable();
|
||||
stop_cpus_in_progress = true;
|
||||
barrier();
|
||||
for_each_cpu(cpu, cpumask) {
|
||||
work = &per_cpu(cpu_stopper.stop_work, cpu);
|
||||
work->fn = fn;
|
||||
|
|
@ -391,6 +392,7 @@ static bool queue_stop_cpus_work(const struct cpumask *cpumask,
|
|||
if (cpu_stop_queue_work(cpu, work))
|
||||
queued = true;
|
||||
}
|
||||
barrier();
|
||||
stop_cpus_in_progress = false;
|
||||
preempt_enable();
|
||||
|
||||
|
|
|
|||
48
kernel/sys.c
48
kernel/sys.c
|
|
@ -105,12 +105,6 @@
|
|||
#ifndef SET_TSC_CTL
|
||||
# define SET_TSC_CTL(a) (-EINVAL)
|
||||
#endif
|
||||
#ifndef MPX_ENABLE_MANAGEMENT
|
||||
# define MPX_ENABLE_MANAGEMENT() (-EINVAL)
|
||||
#endif
|
||||
#ifndef MPX_DISABLE_MANAGEMENT
|
||||
# define MPX_DISABLE_MANAGEMENT() (-EINVAL)
|
||||
#endif
|
||||
#ifndef GET_FP_MODE
|
||||
# define GET_FP_MODE(a) (-EINVAL)
|
||||
#endif
|
||||
|
|
@ -126,6 +120,12 @@
|
|||
#ifndef PAC_RESET_KEYS
|
||||
# define PAC_RESET_KEYS(a, b) (-EINVAL)
|
||||
#endif
|
||||
#ifndef SET_TAGGED_ADDR_CTRL
|
||||
# define SET_TAGGED_ADDR_CTRL(a) (-EINVAL)
|
||||
#endif
|
||||
#ifndef GET_TAGGED_ADDR_CTRL
|
||||
# define GET_TAGGED_ADDR_CTRL() (-EINVAL)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* this is where the system-wide overflow UID and GID are defined, for
|
||||
|
|
@ -1559,15 +1559,6 @@ int do_prlimit(struct task_struct *tsk, unsigned int resource,
|
|||
retval = -EPERM;
|
||||
if (!retval)
|
||||
retval = security_task_setrlimit(tsk, resource, new_rlim);
|
||||
if (resource == RLIMIT_CPU && new_rlim->rlim_cur == 0) {
|
||||
/*
|
||||
* The caller is asking for an immediate RLIMIT_CPU
|
||||
* expiry. But we use the zero value to mean "it was
|
||||
* never set". So let's cheat and make it one second
|
||||
* instead
|
||||
*/
|
||||
new_rlim->rlim_cur = 1;
|
||||
}
|
||||
}
|
||||
if (!retval) {
|
||||
if (old_rlim)
|
||||
|
|
@ -1578,10 +1569,9 @@ int do_prlimit(struct task_struct *tsk, unsigned int resource,
|
|||
task_unlock(tsk->group_leader);
|
||||
|
||||
/*
|
||||
* RLIMIT_CPU handling. Note that the kernel fails to return an error
|
||||
* code if it rejected the user's attempt to set RLIMIT_CPU. This is a
|
||||
* very long-standing error, and fixing it now risks breakage of
|
||||
* applications, so we live with it
|
||||
* RLIMIT_CPU handling. Arm the posix CPU timer if the limit is not
|
||||
* infite. In case of RLIM_INFINITY the posix CPU timer code
|
||||
* ignores the rlimit.
|
||||
*/
|
||||
if (!retval && new_rlim && resource == RLIMIT_CPU &&
|
||||
new_rlim->rlim_cur != RLIM_INFINITY &&
|
||||
|
|
@ -2605,15 +2595,9 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
|
|||
up_write(&me->mm->mmap_sem);
|
||||
break;
|
||||
case PR_MPX_ENABLE_MANAGEMENT:
|
||||
if (arg2 || arg3 || arg4 || arg5)
|
||||
return -EINVAL;
|
||||
error = MPX_ENABLE_MANAGEMENT();
|
||||
break;
|
||||
case PR_MPX_DISABLE_MANAGEMENT:
|
||||
if (arg2 || arg3 || arg4 || arg5)
|
||||
return -EINVAL;
|
||||
error = MPX_DISABLE_MANAGEMENT();
|
||||
break;
|
||||
/* No longer implemented: */
|
||||
return -EINVAL;
|
||||
case PR_SET_FP_MODE:
|
||||
error = SET_FP_MODE(me, arg2);
|
||||
break;
|
||||
|
|
@ -2644,6 +2628,16 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
|
|||
return -EINVAL;
|
||||
error = PAC_RESET_KEYS(me, arg2);
|
||||
break;
|
||||
case PR_SET_TAGGED_ADDR_CTRL:
|
||||
if (arg3 || arg4 || arg5)
|
||||
return -EINVAL;
|
||||
error = SET_TAGGED_ADDR_CTRL(arg2);
|
||||
break;
|
||||
case PR_GET_TAGGED_ADDR_CTRL:
|
||||
if (arg2 || arg3 || arg4 || arg5)
|
||||
return -EINVAL;
|
||||
error = GET_TAGGED_ADDR_CTRL();
|
||||
break;
|
||||
default:
|
||||
error = -EINVAL;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ static int alarmtimer_rtc_add_device(struct device *dev,
|
|||
if (!device_may_wakeup(rtc->dev.parent))
|
||||
return -1;
|
||||
|
||||
__ws = wakeup_source_register("alarmtimer");
|
||||
__ws = wakeup_source_register(dev, "alarmtimer");
|
||||
|
||||
spin_lock_irqsave(&rtcdev_lock, flags);
|
||||
if (!rtcdev) {
|
||||
|
|
@ -432,7 +432,7 @@ int alarm_cancel(struct alarm *alarm)
|
|||
int ret = alarm_try_to_cancel(alarm);
|
||||
if (ret >= 0)
|
||||
return ret;
|
||||
cpu_relax();
|
||||
hrtimer_cancel_wait_running(&alarm->timer);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(alarm_cancel);
|
||||
|
|
@ -605,6 +605,19 @@ static int alarm_timer_try_to_cancel(struct k_itimer *timr)
|
|||
return alarm_try_to_cancel(&timr->it.alarm.alarmtimer);
|
||||
}
|
||||
|
||||
/**
|
||||
* alarm_timer_wait_running - Posix timer callback to wait for a timer
|
||||
* @timr: Pointer to the posixtimer data struct
|
||||
*
|
||||
* Called from the core code when timer cancel detected that the callback
|
||||
* is running. @timr is unlocked and rcu read lock is held to prevent it
|
||||
* from being freed.
|
||||
*/
|
||||
static void alarm_timer_wait_running(struct k_itimer *timr)
|
||||
{
|
||||
hrtimer_cancel_wait_running(&timr->it.alarm.alarmtimer.timer);
|
||||
}
|
||||
|
||||
/**
|
||||
* alarm_timer_arm - Posix timer callback to arm a timer
|
||||
* @timr: Pointer to the posixtimer data struct
|
||||
|
|
@ -672,7 +685,7 @@ static int alarm_timer_create(struct k_itimer *new_timer)
|
|||
enum alarmtimer_type type;
|
||||
|
||||
if (!alarmtimer_get_rtcdev())
|
||||
return -ENOTSUPP;
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!capable(CAP_WAKE_ALARM))
|
||||
return -EPERM;
|
||||
|
|
@ -790,7 +803,7 @@ static int alarm_timer_nsleep(const clockid_t which_clock, int flags,
|
|||
int ret = 0;
|
||||
|
||||
if (!alarmtimer_get_rtcdev())
|
||||
return -ENOTSUPP;
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (flags & ~TIMER_ABSTIME)
|
||||
return -EINVAL;
|
||||
|
|
@ -834,6 +847,7 @@ const struct k_clock alarm_clock = {
|
|||
.timer_forward = alarm_timer_forward,
|
||||
.timer_remaining = alarm_timer_remaining,
|
||||
.timer_try_to_cancel = alarm_timer_try_to_cancel,
|
||||
.timer_wait_running = alarm_timer_wait_running,
|
||||
.nsleep = alarm_timer_nsleep,
|
||||
};
|
||||
#endif /* CONFIG_POSIX_TIMERS */
|
||||
|
|
|
|||
|
|
@ -140,6 +140,11 @@ static struct hrtimer_cpu_base migration_cpu_base = {
|
|||
|
||||
#define migration_base migration_cpu_base.clock_base[0]
|
||||
|
||||
static inline bool is_migration_base(struct hrtimer_clock_base *base)
|
||||
{
|
||||
return base == &migration_base;
|
||||
}
|
||||
|
||||
/*
|
||||
* We are using hashed locking: holding per_cpu(hrtimer_bases)[n].lock
|
||||
* means that all timers which are tied to this base via timer->base are
|
||||
|
|
@ -264,6 +269,11 @@ again:
|
|||
|
||||
#else /* CONFIG_SMP */
|
||||
|
||||
static inline bool is_migration_base(struct hrtimer_clock_base *base)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline struct hrtimer_clock_base *
|
||||
lock_hrtimer_base(const struct hrtimer *timer, unsigned long *flags)
|
||||
{
|
||||
|
|
@ -427,6 +437,17 @@ void hrtimer_init_on_stack(struct hrtimer *timer, clockid_t clock_id,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(hrtimer_init_on_stack);
|
||||
|
||||
static void __hrtimer_init_sleeper(struct hrtimer_sleeper *sl,
|
||||
clockid_t clock_id, enum hrtimer_mode mode);
|
||||
|
||||
void hrtimer_init_sleeper_on_stack(struct hrtimer_sleeper *sl,
|
||||
clockid_t clock_id, enum hrtimer_mode mode)
|
||||
{
|
||||
debug_object_init_on_stack(&sl->timer, &hrtimer_debug_descr);
|
||||
__hrtimer_init_sleeper(sl, clock_id, mode);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hrtimer_init_sleeper_on_stack);
|
||||
|
||||
void destroy_hrtimer_on_stack(struct hrtimer *timer)
|
||||
{
|
||||
debug_object_free(timer, &hrtimer_debug_descr);
|
||||
|
|
@ -1096,9 +1117,13 @@ void hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim,
|
|||
|
||||
/*
|
||||
* Check whether the HRTIMER_MODE_SOFT bit and hrtimer.is_soft
|
||||
* match.
|
||||
* match on CONFIG_PREEMPT_RT = n. With PREEMPT_RT check the hard
|
||||
* expiry mode because unmarked timers are moved to softirq expiry.
|
||||
*/
|
||||
WARN_ON_ONCE(!(mode & HRTIMER_MODE_SOFT) ^ !timer->is_soft);
|
||||
if (!IS_ENABLED(CONFIG_PREEMPT_RT))
|
||||
WARN_ON_ONCE(!(mode & HRTIMER_MODE_SOFT) ^ !timer->is_soft);
|
||||
else
|
||||
WARN_ON_ONCE(!(mode & HRTIMER_MODE_HARD) ^ !timer->is_hard);
|
||||
|
||||
base = lock_hrtimer_base(timer, &flags);
|
||||
|
||||
|
|
@ -1147,6 +1172,93 @@ int hrtimer_try_to_cancel(struct hrtimer *timer)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(hrtimer_try_to_cancel);
|
||||
|
||||
#ifdef CONFIG_PREEMPT_RT
|
||||
static void hrtimer_cpu_base_init_expiry_lock(struct hrtimer_cpu_base *base)
|
||||
{
|
||||
spin_lock_init(&base->softirq_expiry_lock);
|
||||
}
|
||||
|
||||
static void hrtimer_cpu_base_lock_expiry(struct hrtimer_cpu_base *base)
|
||||
{
|
||||
spin_lock(&base->softirq_expiry_lock);
|
||||
}
|
||||
|
||||
static void hrtimer_cpu_base_unlock_expiry(struct hrtimer_cpu_base *base)
|
||||
{
|
||||
spin_unlock(&base->softirq_expiry_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* The counterpart to hrtimer_cancel_wait_running().
|
||||
*
|
||||
* If there is a waiter for cpu_base->expiry_lock, then it was waiting for
|
||||
* the timer callback to finish. Drop expiry_lock and reaquire it. That
|
||||
* allows the waiter to acquire the lock and make progress.
|
||||
*/
|
||||
static void hrtimer_sync_wait_running(struct hrtimer_cpu_base *cpu_base,
|
||||
unsigned long flags)
|
||||
{
|
||||
if (atomic_read(&cpu_base->timer_waiters)) {
|
||||
raw_spin_unlock_irqrestore(&cpu_base->lock, flags);
|
||||
spin_unlock(&cpu_base->softirq_expiry_lock);
|
||||
spin_lock(&cpu_base->softirq_expiry_lock);
|
||||
raw_spin_lock_irq(&cpu_base->lock);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is called on PREEMPT_RT kernels when the fast path
|
||||
* deletion of a timer failed because the timer callback function was
|
||||
* running.
|
||||
*
|
||||
* This prevents priority inversion: if the soft irq thread is preempted
|
||||
* in the middle of a timer callback, then calling del_timer_sync() can
|
||||
* lead to two issues:
|
||||
*
|
||||
* - If the caller is on a remote CPU then it has to spin wait for the timer
|
||||
* handler to complete. This can result in unbound priority inversion.
|
||||
*
|
||||
* - If the caller originates from the task which preempted the timer
|
||||
* handler on the same CPU, then spin waiting for the timer handler to
|
||||
* complete is never going to end.
|
||||
*/
|
||||
void hrtimer_cancel_wait_running(const struct hrtimer *timer)
|
||||
{
|
||||
/* Lockless read. Prevent the compiler from reloading it below */
|
||||
struct hrtimer_clock_base *base = READ_ONCE(timer->base);
|
||||
|
||||
/*
|
||||
* Just relax if the timer expires in hard interrupt context or if
|
||||
* it is currently on the migration base.
|
||||
*/
|
||||
if (!timer->is_soft || is_migration_base(base)) {
|
||||
cpu_relax();
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Mark the base as contended and grab the expiry lock, which is
|
||||
* held by the softirq across the timer callback. Drop the lock
|
||||
* immediately so the softirq can expire the next timer. In theory
|
||||
* the timer could already be running again, but that's more than
|
||||
* unlikely and just causes another wait loop.
|
||||
*/
|
||||
atomic_inc(&base->cpu_base->timer_waiters);
|
||||
spin_lock_bh(&base->cpu_base->softirq_expiry_lock);
|
||||
atomic_dec(&base->cpu_base->timer_waiters);
|
||||
spin_unlock_bh(&base->cpu_base->softirq_expiry_lock);
|
||||
}
|
||||
#else
|
||||
static inline void
|
||||
hrtimer_cpu_base_init_expiry_lock(struct hrtimer_cpu_base *base) { }
|
||||
static inline void
|
||||
hrtimer_cpu_base_lock_expiry(struct hrtimer_cpu_base *base) { }
|
||||
static inline void
|
||||
hrtimer_cpu_base_unlock_expiry(struct hrtimer_cpu_base *base) { }
|
||||
static inline void hrtimer_sync_wait_running(struct hrtimer_cpu_base *base,
|
||||
unsigned long flags) { }
|
||||
#endif
|
||||
|
||||
/**
|
||||
* hrtimer_cancel - cancel a timer and wait for the handler to finish.
|
||||
* @timer: the timer to be cancelled
|
||||
|
|
@ -1157,13 +1269,15 @@ EXPORT_SYMBOL_GPL(hrtimer_try_to_cancel);
|
|||
*/
|
||||
int hrtimer_cancel(struct hrtimer *timer)
|
||||
{
|
||||
for (;;) {
|
||||
int ret = hrtimer_try_to_cancel(timer);
|
||||
int ret;
|
||||
|
||||
if (ret >= 0)
|
||||
return ret;
|
||||
cpu_relax();
|
||||
}
|
||||
do {
|
||||
ret = hrtimer_try_to_cancel(timer);
|
||||
|
||||
if (ret < 0)
|
||||
hrtimer_cancel_wait_running(timer);
|
||||
} while (ret < 0);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hrtimer_cancel);
|
||||
|
||||
|
|
@ -1260,8 +1374,17 @@ static void __hrtimer_init(struct hrtimer *timer, clockid_t clock_id,
|
|||
enum hrtimer_mode mode)
|
||||
{
|
||||
bool softtimer = !!(mode & HRTIMER_MODE_SOFT);
|
||||
int base = softtimer ? HRTIMER_MAX_CLOCK_BASES / 2 : 0;
|
||||
struct hrtimer_cpu_base *cpu_base;
|
||||
int base;
|
||||
|
||||
/*
|
||||
* On PREEMPT_RT enabled kernels hrtimers which are not explicitely
|
||||
* marked for hard interrupt expiry mode are moved into soft
|
||||
* interrupt context for latency reasons and because the callbacks
|
||||
* can invoke functions which might sleep on RT, e.g. spin_lock().
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_PREEMPT_RT) && !(mode & HRTIMER_MODE_HARD))
|
||||
softtimer = true;
|
||||
|
||||
memset(timer, 0, sizeof(struct hrtimer));
|
||||
|
||||
|
|
@ -1275,8 +1398,10 @@ static void __hrtimer_init(struct hrtimer *timer, clockid_t clock_id,
|
|||
if (clock_id == CLOCK_REALTIME && mode & HRTIMER_MODE_REL)
|
||||
clock_id = CLOCK_MONOTONIC;
|
||||
|
||||
base = softtimer ? HRTIMER_MAX_CLOCK_BASES / 2 : 0;
|
||||
base += hrtimer_clockid_to_base(clock_id);
|
||||
timer->is_soft = softtimer;
|
||||
timer->is_hard = !softtimer;
|
||||
timer->base = &cpu_base->clock_base[base];
|
||||
timerqueue_init(&timer->node);
|
||||
}
|
||||
|
|
@ -1449,6 +1574,8 @@ static void __hrtimer_run_queues(struct hrtimer_cpu_base *cpu_base, ktime_t now,
|
|||
break;
|
||||
|
||||
__run_hrtimer(cpu_base, base, timer, &basenow, flags);
|
||||
if (active_mask == HRTIMER_ACTIVE_SOFT)
|
||||
hrtimer_sync_wait_running(cpu_base, flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1459,6 +1586,7 @@ static __latent_entropy void hrtimer_run_softirq(struct softirq_action *h)
|
|||
unsigned long flags;
|
||||
ktime_t now;
|
||||
|
||||
hrtimer_cpu_base_lock_expiry(cpu_base);
|
||||
raw_spin_lock_irqsave(&cpu_base->lock, flags);
|
||||
|
||||
now = hrtimer_update_base(cpu_base);
|
||||
|
|
@ -1468,6 +1596,7 @@ static __latent_entropy void hrtimer_run_softirq(struct softirq_action *h)
|
|||
hrtimer_update_softirq_timer(cpu_base, true);
|
||||
|
||||
raw_spin_unlock_irqrestore(&cpu_base->lock, flags);
|
||||
hrtimer_cpu_base_unlock_expiry(cpu_base);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HIGH_RES_TIMERS
|
||||
|
|
@ -1639,10 +1768,75 @@ static enum hrtimer_restart hrtimer_wakeup(struct hrtimer *timer)
|
|||
return HRTIMER_NORESTART;
|
||||
}
|
||||
|
||||
void hrtimer_init_sleeper(struct hrtimer_sleeper *sl, struct task_struct *task)
|
||||
/**
|
||||
* hrtimer_sleeper_start_expires - Start a hrtimer sleeper timer
|
||||
* @sl: sleeper to be started
|
||||
* @mode: timer mode abs/rel
|
||||
*
|
||||
* Wrapper around hrtimer_start_expires() for hrtimer_sleeper based timers
|
||||
* to allow PREEMPT_RT to tweak the delivery mode (soft/hardirq context)
|
||||
*/
|
||||
void hrtimer_sleeper_start_expires(struct hrtimer_sleeper *sl,
|
||||
enum hrtimer_mode mode)
|
||||
{
|
||||
/*
|
||||
* Make the enqueue delivery mode check work on RT. If the sleeper
|
||||
* was initialized for hard interrupt delivery, force the mode bit.
|
||||
* This is a special case for hrtimer_sleepers because
|
||||
* hrtimer_init_sleeper() determines the delivery mode on RT so the
|
||||
* fiddling with this decision is avoided at the call sites.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_PREEMPT_RT) && sl->timer.is_hard)
|
||||
mode |= HRTIMER_MODE_HARD;
|
||||
|
||||
hrtimer_start_expires(&sl->timer, mode);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hrtimer_sleeper_start_expires);
|
||||
|
||||
static void __hrtimer_init_sleeper(struct hrtimer_sleeper *sl,
|
||||
clockid_t clock_id, enum hrtimer_mode mode)
|
||||
{
|
||||
/*
|
||||
* On PREEMPT_RT enabled kernels hrtimers which are not explicitely
|
||||
* marked for hard interrupt expiry mode are moved into soft
|
||||
* interrupt context either for latency reasons or because the
|
||||
* hrtimer callback takes regular spinlocks or invokes other
|
||||
* functions which are not suitable for hard interrupt context on
|
||||
* PREEMPT_RT.
|
||||
*
|
||||
* The hrtimer_sleeper callback is RT compatible in hard interrupt
|
||||
* context, but there is a latency concern: Untrusted userspace can
|
||||
* spawn many threads which arm timers for the same expiry time on
|
||||
* the same CPU. That causes a latency spike due to the wakeup of
|
||||
* a gazillion threads.
|
||||
*
|
||||
* OTOH, priviledged real-time user space applications rely on the
|
||||
* low latency of hard interrupt wakeups. If the current task is in
|
||||
* a real-time scheduling class, mark the mode for hard interrupt
|
||||
* expiry.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_PREEMPT_RT)) {
|
||||
if (task_is_realtime(current) && !(mode & HRTIMER_MODE_SOFT))
|
||||
mode |= HRTIMER_MODE_HARD;
|
||||
}
|
||||
|
||||
__hrtimer_init(&sl->timer, clock_id, mode);
|
||||
sl->timer.function = hrtimer_wakeup;
|
||||
sl->task = task;
|
||||
sl->task = current;
|
||||
}
|
||||
|
||||
/**
|
||||
* hrtimer_init_sleeper - initialize sleeper to the given clock
|
||||
* @sl: sleeper to be initialized
|
||||
* @clock_id: the clock to be used
|
||||
* @mode: timer mode abs/rel
|
||||
*/
|
||||
void hrtimer_init_sleeper(struct hrtimer_sleeper *sl, clockid_t clock_id,
|
||||
enum hrtimer_mode mode)
|
||||
{
|
||||
debug_init(&sl->timer, clock_id, mode);
|
||||
__hrtimer_init_sleeper(sl, clock_id, mode);
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(hrtimer_init_sleeper);
|
||||
|
||||
|
|
@ -1669,11 +1863,9 @@ static int __sched do_nanosleep(struct hrtimer_sleeper *t, enum hrtimer_mode mod
|
|||
{
|
||||
struct restart_block *restart;
|
||||
|
||||
hrtimer_init_sleeper(t, current);
|
||||
|
||||
do {
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
hrtimer_start_expires(&t->timer, mode);
|
||||
hrtimer_sleeper_start_expires(t, mode);
|
||||
|
||||
if (likely(t->task))
|
||||
freezable_schedule();
|
||||
|
|
@ -1707,10 +1899,9 @@ static long __sched hrtimer_nanosleep_restart(struct restart_block *restart)
|
|||
struct hrtimer_sleeper t;
|
||||
int ret;
|
||||
|
||||
hrtimer_init_on_stack(&t.timer, restart->nanosleep.clockid,
|
||||
HRTIMER_MODE_ABS);
|
||||
hrtimer_init_sleeper_on_stack(&t, restart->nanosleep.clockid,
|
||||
HRTIMER_MODE_ABS);
|
||||
hrtimer_set_expires_tv64(&t.timer, restart->nanosleep.expires);
|
||||
|
||||
ret = do_nanosleep(&t, HRTIMER_MODE_ABS);
|
||||
destroy_hrtimer_on_stack(&t.timer);
|
||||
return ret;
|
||||
|
|
@ -1728,7 +1919,7 @@ long hrtimer_nanosleep(const struct timespec64 *rqtp,
|
|||
if (dl_task(current) || rt_task(current))
|
||||
slack = 0;
|
||||
|
||||
hrtimer_init_on_stack(&t.timer, clockid, mode);
|
||||
hrtimer_init_sleeper_on_stack(&t, clockid, mode);
|
||||
hrtimer_set_expires_range_ns(&t.timer, timespec64_to_ktime(*rqtp), slack);
|
||||
ret = do_nanosleep(&t, mode);
|
||||
if (ret != -ERESTART_RESTARTBLOCK)
|
||||
|
|
@ -1809,6 +2000,7 @@ int hrtimers_prepare_cpu(unsigned int cpu)
|
|||
cpu_base->softirq_next_timer = NULL;
|
||||
cpu_base->expires_next = KTIME_MAX;
|
||||
cpu_base->softirq_expires_next = KTIME_MAX;
|
||||
hrtimer_cpu_base_init_expiry_lock(cpu_base);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1927,12 +2119,9 @@ schedule_hrtimeout_range_clock(ktime_t *expires, u64 delta,
|
|||
return -EINTR;
|
||||
}
|
||||
|
||||
hrtimer_init_on_stack(&t.timer, clock_id, mode);
|
||||
hrtimer_init_sleeper_on_stack(&t, clock_id, mode);
|
||||
hrtimer_set_expires_range_ns(&t.timer, *expires, delta);
|
||||
|
||||
hrtimer_init_sleeper(&t, current);
|
||||
|
||||
hrtimer_start_expires(&t.timer, mode);
|
||||
hrtimer_sleeper_start_expires(&t, mode);
|
||||
|
||||
if (likely(t.task))
|
||||
schedule();
|
||||
|
|
|
|||
|
|
@ -55,15 +55,10 @@ static void get_cpu_itimer(struct task_struct *tsk, unsigned int clock_id,
|
|||
val = it->expires;
|
||||
interval = it->incr;
|
||||
if (val) {
|
||||
struct task_cputime cputime;
|
||||
u64 t;
|
||||
u64 t, samples[CPUCLOCK_MAX];
|
||||
|
||||
thread_group_cputimer(tsk, &cputime);
|
||||
if (clock_id == CPUCLOCK_PROF)
|
||||
t = cputime.utime + cputime.stime;
|
||||
else
|
||||
/* CPUCLOCK_VIRT */
|
||||
t = cputime.utime;
|
||||
thread_group_sample_cputime(tsk, samples);
|
||||
t = samples[clock_id];
|
||||
|
||||
if (val < t)
|
||||
/* about to fire */
|
||||
|
|
@ -213,6 +208,7 @@ again:
|
|||
/* We are sharing ->siglock with it_real_fn() */
|
||||
if (hrtimer_try_to_cancel(timer) < 0) {
|
||||
spin_unlock_irq(&tsk->sighand->siglock);
|
||||
hrtimer_cancel_wait_running(timer);
|
||||
goto again;
|
||||
}
|
||||
expires = timeval_to_ktime(value->it_value);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -442,7 +442,7 @@ static struct k_itimer * alloc_posix_timer(void)
|
|||
|
||||
static void k_itimer_rcu_free(struct rcu_head *head)
|
||||
{
|
||||
struct k_itimer *tmr = container_of(head, struct k_itimer, it.rcu);
|
||||
struct k_itimer *tmr = container_of(head, struct k_itimer, rcu);
|
||||
|
||||
kmem_cache_free(posix_timers_cache, tmr);
|
||||
}
|
||||
|
|
@ -459,7 +459,7 @@ static void release_posix_timer(struct k_itimer *tmr, int it_id_set)
|
|||
}
|
||||
put_pid(tmr->it_pid);
|
||||
sigqueue_free(tmr->sigq);
|
||||
call_rcu(&tmr->it.rcu, k_itimer_rcu_free);
|
||||
call_rcu(&tmr->rcu, k_itimer_rcu_free);
|
||||
}
|
||||
|
||||
static int common_timer_create(struct k_itimer *new_timer)
|
||||
|
|
@ -805,6 +805,35 @@ static int common_hrtimer_try_to_cancel(struct k_itimer *timr)
|
|||
return hrtimer_try_to_cancel(&timr->it.real.timer);
|
||||
}
|
||||
|
||||
static void common_timer_wait_running(struct k_itimer *timer)
|
||||
{
|
||||
hrtimer_cancel_wait_running(&timer->it.real.timer);
|
||||
}
|
||||
|
||||
/*
|
||||
* On PREEMPT_RT this prevent priority inversion against softirq kthread in
|
||||
* case it gets preempted while executing a timer callback. See comments in
|
||||
* hrtimer_cancel_wait_running. For PREEMPT_RT=n this just results in a
|
||||
* cpu_relax().
|
||||
*/
|
||||
static struct k_itimer *timer_wait_running(struct k_itimer *timer,
|
||||
unsigned long *flags)
|
||||
{
|
||||
const struct k_clock *kc = READ_ONCE(timer->kclock);
|
||||
timer_t timer_id = READ_ONCE(timer->it_id);
|
||||
|
||||
/* Prevent kfree(timer) after dropping the lock */
|
||||
rcu_read_lock();
|
||||
unlock_timer(timer, *flags);
|
||||
|
||||
if (!WARN_ON_ONCE(!kc->timer_wait_running))
|
||||
kc->timer_wait_running(timer);
|
||||
|
||||
rcu_read_unlock();
|
||||
/* Relock the timer. It might be not longer hashed. */
|
||||
return lock_timer(timer_id, flags);
|
||||
}
|
||||
|
||||
/* Set a POSIX.1b interval timer. */
|
||||
int common_timer_set(struct k_itimer *timr, int flags,
|
||||
struct itimerspec64 *new_setting,
|
||||
|
|
@ -844,13 +873,13 @@ int common_timer_set(struct k_itimer *timr, int flags,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int do_timer_settime(timer_t timer_id, int flags,
|
||||
static int do_timer_settime(timer_t timer_id, int tmr_flags,
|
||||
struct itimerspec64 *new_spec64,
|
||||
struct itimerspec64 *old_spec64)
|
||||
{
|
||||
const struct k_clock *kc;
|
||||
struct k_itimer *timr;
|
||||
unsigned long flag;
|
||||
unsigned long flags;
|
||||
int error = 0;
|
||||
|
||||
if (!timespec64_valid(&new_spec64->it_interval) ||
|
||||
|
|
@ -859,8 +888,9 @@ static int do_timer_settime(timer_t timer_id, int flags,
|
|||
|
||||
if (old_spec64)
|
||||
memset(old_spec64, 0, sizeof(*old_spec64));
|
||||
|
||||
timr = lock_timer(timer_id, &flags);
|
||||
retry:
|
||||
timr = lock_timer(timer_id, &flag);
|
||||
if (!timr)
|
||||
return -EINVAL;
|
||||
|
||||
|
|
@ -868,13 +898,16 @@ retry:
|
|||
if (WARN_ON_ONCE(!kc || !kc->timer_set))
|
||||
error = -EINVAL;
|
||||
else
|
||||
error = kc->timer_set(timr, flags, new_spec64, old_spec64);
|
||||
error = kc->timer_set(timr, tmr_flags, new_spec64, old_spec64);
|
||||
|
||||
unlock_timer(timr, flag);
|
||||
if (error == TIMER_RETRY) {
|
||||
old_spec64 = NULL; // We already got the old time...
|
||||
// We already got the old time...
|
||||
old_spec64 = NULL;
|
||||
/* Unlocks and relocks the timer if it still exists */
|
||||
timr = timer_wait_running(timr, &flags);
|
||||
goto retry;
|
||||
}
|
||||
unlock_timer(timr, flags);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
|
@ -951,13 +984,15 @@ SYSCALL_DEFINE1(timer_delete, timer_t, timer_id)
|
|||
struct k_itimer *timer;
|
||||
unsigned long flags;
|
||||
|
||||
retry_delete:
|
||||
timer = lock_timer(timer_id, &flags);
|
||||
|
||||
retry_delete:
|
||||
if (!timer)
|
||||
return -EINVAL;
|
||||
|
||||
if (timer_delete_hook(timer) == TIMER_RETRY) {
|
||||
unlock_timer(timer, flags);
|
||||
if (unlikely(timer_delete_hook(timer) == TIMER_RETRY)) {
|
||||
/* Unlocks and relocks the timer if it still exists */
|
||||
timer = timer_wait_running(timer, &flags);
|
||||
goto retry_delete;
|
||||
}
|
||||
|
||||
|
|
@ -1238,6 +1273,7 @@ static const struct k_clock clock_realtime = {
|
|||
.timer_forward = common_hrtimer_forward,
|
||||
.timer_remaining = common_hrtimer_remaining,
|
||||
.timer_try_to_cancel = common_hrtimer_try_to_cancel,
|
||||
.timer_wait_running = common_timer_wait_running,
|
||||
.timer_arm = common_hrtimer_arm,
|
||||
};
|
||||
|
||||
|
|
@ -1253,6 +1289,7 @@ static const struct k_clock clock_monotonic = {
|
|||
.timer_forward = common_hrtimer_forward,
|
||||
.timer_remaining = common_hrtimer_remaining,
|
||||
.timer_try_to_cancel = common_hrtimer_try_to_cancel,
|
||||
.timer_wait_running = common_timer_wait_running,
|
||||
.timer_arm = common_hrtimer_arm,
|
||||
};
|
||||
|
||||
|
|
@ -1283,6 +1320,7 @@ static const struct k_clock clock_tai = {
|
|||
.timer_forward = common_hrtimer_forward,
|
||||
.timer_remaining = common_hrtimer_remaining,
|
||||
.timer_try_to_cancel = common_hrtimer_try_to_cancel,
|
||||
.timer_wait_running = common_timer_wait_running,
|
||||
.timer_arm = common_hrtimer_arm,
|
||||
};
|
||||
|
||||
|
|
@ -1298,6 +1336,7 @@ static const struct k_clock clock_boottime = {
|
|||
.timer_forward = common_hrtimer_forward,
|
||||
.timer_remaining = common_hrtimer_remaining,
|
||||
.timer_try_to_cancel = common_hrtimer_try_to_cancel,
|
||||
.timer_wait_running = common_timer_wait_running,
|
||||
.timer_arm = common_hrtimer_arm,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ struct k_clock {
|
|||
int (*timer_try_to_cancel)(struct k_itimer *timr);
|
||||
void (*timer_arm)(struct k_itimer *timr, ktime_t expires,
|
||||
bool absolute, bool sigev_none);
|
||||
void (*timer_wait_running)(struct k_itimer *timr);
|
||||
};
|
||||
|
||||
extern const struct k_clock clock_posix_cpu;
|
||||
|
|
|
|||
|
|
@ -59,11 +59,16 @@ static int bc_set_next(ktime_t expires, struct clock_event_device *bc)
|
|||
* hrtimer_{start/cancel} functions call into tracing,
|
||||
* calls to these functions must be bound within RCU_NONIDLE.
|
||||
*/
|
||||
RCU_NONIDLE({
|
||||
RCU_NONIDLE(
|
||||
{
|
||||
bc_moved = hrtimer_try_to_cancel(&bctimer) >= 0;
|
||||
if (bc_moved)
|
||||
if (bc_moved) {
|
||||
hrtimer_start(&bctimer, expires,
|
||||
HRTIMER_MODE_ABS_PINNED);});
|
||||
HRTIMER_MODE_ABS_PINNED_HARD);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (bc_moved) {
|
||||
/* Bind the "device" to the cpu */
|
||||
bc->bound_on = smp_processor_id();
|
||||
|
|
@ -104,7 +109,7 @@ static enum hrtimer_restart bc_handler(struct hrtimer *t)
|
|||
|
||||
void tick_setup_hrtimer_broadcast(void)
|
||||
{
|
||||
hrtimer_init(&bctimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
|
||||
hrtimer_init(&bctimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_HARD);
|
||||
bctimer.function = bc_handler;
|
||||
clockevents_register_device(&ce_broadcast_hrtimer);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -634,10 +634,12 @@ static void tick_nohz_restart(struct tick_sched *ts, ktime_t now)
|
|||
/* Forward the time to expire in the future */
|
||||
hrtimer_forward(&ts->sched_timer, now, tick_period);
|
||||
|
||||
if (ts->nohz_mode == NOHZ_MODE_HIGHRES)
|
||||
hrtimer_start_expires(&ts->sched_timer, HRTIMER_MODE_ABS_PINNED);
|
||||
else
|
||||
if (ts->nohz_mode == NOHZ_MODE_HIGHRES) {
|
||||
hrtimer_start_expires(&ts->sched_timer,
|
||||
HRTIMER_MODE_ABS_PINNED_HARD);
|
||||
} else {
|
||||
tick_program_event(hrtimer_get_expires(&ts->sched_timer), 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset to make sure next tick stop doesn't get fooled by past
|
||||
|
|
@ -802,7 +804,8 @@ static void tick_nohz_stop_tick(struct tick_sched *ts, int cpu)
|
|||
}
|
||||
|
||||
if (ts->nohz_mode == NOHZ_MODE_HIGHRES) {
|
||||
hrtimer_start(&ts->sched_timer, tick, HRTIMER_MODE_ABS_PINNED);
|
||||
hrtimer_start(&ts->sched_timer, tick,
|
||||
HRTIMER_MODE_ABS_PINNED_HARD);
|
||||
} else {
|
||||
hrtimer_set_expires(&ts->sched_timer, tick);
|
||||
tick_program_event(tick, 1);
|
||||
|
|
@ -1230,7 +1233,7 @@ static void tick_nohz_switch_to_nohz(void)
|
|||
* Recycle the hrtimer in ts, so we can share the
|
||||
* hrtimer_forward with the highres code.
|
||||
*/
|
||||
hrtimer_init(&ts->sched_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
|
||||
hrtimer_init(&ts->sched_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_HARD);
|
||||
/* Get the next period */
|
||||
next = tick_init_jiffy_update();
|
||||
|
||||
|
|
@ -1327,7 +1330,7 @@ void tick_setup_sched_timer(void)
|
|||
/*
|
||||
* Emulate tick processing via per-CPU hrtimers:
|
||||
*/
|
||||
hrtimer_init(&ts->sched_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
|
||||
hrtimer_init(&ts->sched_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_HARD);
|
||||
ts->sched_timer.function = tick_sched_timer;
|
||||
|
||||
/* Get the next period (per-CPU) */
|
||||
|
|
@ -1342,7 +1345,7 @@ void tick_setup_sched_timer(void)
|
|||
}
|
||||
|
||||
hrtimer_forward(&ts->sched_timer, now, tick_period);
|
||||
hrtimer_start_expires(&ts->sched_timer, HRTIMER_MODE_ABS_PINNED);
|
||||
hrtimer_start_expires(&ts->sched_timer, HRTIMER_MODE_ABS_PINNED_HARD);
|
||||
tick_nohz_activate(ts, NOHZ_MODE_HIGHRES);
|
||||
}
|
||||
#endif /* HIGH_RES_TIMERS */
|
||||
|
|
|
|||
|
|
@ -196,6 +196,10 @@ EXPORT_SYMBOL(jiffies_64);
|
|||
struct timer_base {
|
||||
raw_spinlock_t lock;
|
||||
struct timer_list *running_timer;
|
||||
#ifdef CONFIG_PREEMPT_RT
|
||||
spinlock_t expiry_lock;
|
||||
atomic_t timer_waiters;
|
||||
#endif
|
||||
unsigned long clk;
|
||||
unsigned long next_expiry;
|
||||
unsigned int cpu;
|
||||
|
|
@ -1227,7 +1231,78 @@ int try_to_del_timer_sync(struct timer_list *timer)
|
|||
}
|
||||
EXPORT_SYMBOL(try_to_del_timer_sync);
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
#ifdef CONFIG_PREEMPT_RT
|
||||
static __init void timer_base_init_expiry_lock(struct timer_base *base)
|
||||
{
|
||||
spin_lock_init(&base->expiry_lock);
|
||||
}
|
||||
|
||||
static inline void timer_base_lock_expiry(struct timer_base *base)
|
||||
{
|
||||
spin_lock(&base->expiry_lock);
|
||||
}
|
||||
|
||||
static inline void timer_base_unlock_expiry(struct timer_base *base)
|
||||
{
|
||||
spin_unlock(&base->expiry_lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* The counterpart to del_timer_wait_running().
|
||||
*
|
||||
* If there is a waiter for base->expiry_lock, then it was waiting for the
|
||||
* timer callback to finish. Drop expiry_lock and reaquire it. That allows
|
||||
* the waiter to acquire the lock and make progress.
|
||||
*/
|
||||
static void timer_sync_wait_running(struct timer_base *base)
|
||||
{
|
||||
if (atomic_read(&base->timer_waiters)) {
|
||||
spin_unlock(&base->expiry_lock);
|
||||
spin_lock(&base->expiry_lock);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is called on PREEMPT_RT kernels when the fast path
|
||||
* deletion of a timer failed because the timer callback function was
|
||||
* running.
|
||||
*
|
||||
* This prevents priority inversion, if the softirq thread on a remote CPU
|
||||
* got preempted, and it prevents a life lock when the task which tries to
|
||||
* delete a timer preempted the softirq thread running the timer callback
|
||||
* function.
|
||||
*/
|
||||
static void del_timer_wait_running(struct timer_list *timer)
|
||||
{
|
||||
u32 tf;
|
||||
|
||||
tf = READ_ONCE(timer->flags);
|
||||
if (!(tf & TIMER_MIGRATING)) {
|
||||
struct timer_base *base = get_timer_base(tf);
|
||||
|
||||
/*
|
||||
* Mark the base as contended and grab the expiry lock,
|
||||
* which is held by the softirq across the timer
|
||||
* callback. Drop the lock immediately so the softirq can
|
||||
* expire the next timer. In theory the timer could already
|
||||
* be running again, but that's more than unlikely and just
|
||||
* causes another wait loop.
|
||||
*/
|
||||
atomic_inc(&base->timer_waiters);
|
||||
spin_lock_bh(&base->expiry_lock);
|
||||
atomic_dec(&base->timer_waiters);
|
||||
spin_unlock_bh(&base->expiry_lock);
|
||||
}
|
||||
}
|
||||
#else
|
||||
static inline void timer_base_init_expiry_lock(struct timer_base *base) { }
|
||||
static inline void timer_base_lock_expiry(struct timer_base *base) { }
|
||||
static inline void timer_base_unlock_expiry(struct timer_base *base) { }
|
||||
static inline void timer_sync_wait_running(struct timer_base *base) { }
|
||||
static inline void del_timer_wait_running(struct timer_list *timer) { }
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT_RT)
|
||||
/**
|
||||
* del_timer_sync - deactivate a timer and wait for the handler to finish.
|
||||
* @timer: the timer to be deactivated
|
||||
|
|
@ -1266,6 +1341,8 @@ EXPORT_SYMBOL(try_to_del_timer_sync);
|
|||
*/
|
||||
int del_timer_sync(struct timer_list *timer)
|
||||
{
|
||||
int ret;
|
||||
|
||||
#ifdef CONFIG_LOCKDEP
|
||||
unsigned long flags;
|
||||
|
||||
|
|
@ -1283,12 +1360,17 @@ int del_timer_sync(struct timer_list *timer)
|
|||
* could lead to deadlock.
|
||||
*/
|
||||
WARN_ON(in_irq() && !(timer->flags & TIMER_IRQSAFE));
|
||||
for (;;) {
|
||||
int ret = try_to_del_timer_sync(timer);
|
||||
if (ret >= 0)
|
||||
return ret;
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
do {
|
||||
ret = try_to_del_timer_sync(timer);
|
||||
|
||||
if (unlikely(ret < 0)) {
|
||||
del_timer_wait_running(timer);
|
||||
cpu_relax();
|
||||
}
|
||||
} while (ret < 0);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(del_timer_sync);
|
||||
#endif
|
||||
|
|
@ -1360,10 +1442,13 @@ static void expire_timers(struct timer_base *base, struct hlist_head *head)
|
|||
if (timer->flags & TIMER_IRQSAFE) {
|
||||
raw_spin_unlock(&base->lock);
|
||||
call_timer_fn(timer, fn, baseclk);
|
||||
base->running_timer = NULL;
|
||||
raw_spin_lock(&base->lock);
|
||||
} else {
|
||||
raw_spin_unlock_irq(&base->lock);
|
||||
call_timer_fn(timer, fn, baseclk);
|
||||
base->running_timer = NULL;
|
||||
timer_sync_wait_running(base);
|
||||
raw_spin_lock_irq(&base->lock);
|
||||
}
|
||||
}
|
||||
|
|
@ -1643,7 +1728,7 @@ void update_process_times(int user_tick)
|
|||
#endif
|
||||
scheduler_tick();
|
||||
if (IS_ENABLED(CONFIG_POSIX_TIMERS))
|
||||
run_posix_cpu_timers(p);
|
||||
run_posix_cpu_timers();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1658,6 +1743,7 @@ static inline void __run_timers(struct timer_base *base)
|
|||
if (!time_after_eq(jiffies, base->clk))
|
||||
return;
|
||||
|
||||
timer_base_lock_expiry(base);
|
||||
raw_spin_lock_irq(&base->lock);
|
||||
|
||||
/*
|
||||
|
|
@ -1684,8 +1770,8 @@ static inline void __run_timers(struct timer_base *base)
|
|||
while (levels--)
|
||||
expire_timers(base, heads + levels);
|
||||
}
|
||||
base->running_timer = NULL;
|
||||
raw_spin_unlock_irq(&base->lock);
|
||||
timer_base_unlock_expiry(base);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1930,6 +2016,7 @@ static void __init init_timer_cpu(int cpu)
|
|||
base->cpu = cpu;
|
||||
raw_spin_lock_init(&base->lock);
|
||||
base->clk = jiffies;
|
||||
timer_base_init_expiry_lock(base);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -263,7 +263,6 @@ static void torture_onoff_cleanup(void)
|
|||
onoff_task = NULL;
|
||||
#endif /* #ifdef CONFIG_HOTPLUG_CPU */
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(torture_onoff_cleanup);
|
||||
|
||||
/*
|
||||
* Print online/offline testing statistics.
|
||||
|
|
@ -449,7 +448,6 @@ static void torture_shuffle_cleanup(void)
|
|||
}
|
||||
shuffler_task = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(torture_shuffle_cleanup);
|
||||
|
||||
/*
|
||||
* Variables for auto-shutdown. This allows "lights out" torture runs
|
||||
|
|
|
|||
|
|
@ -146,7 +146,7 @@ config FUNCTION_TRACER
|
|||
select GENERIC_TRACER
|
||||
select CONTEXT_SWITCH_TRACER
|
||||
select GLOB
|
||||
select TASKS_RCU if PREEMPT
|
||||
select TASKS_RCU if PREEMPTION
|
||||
help
|
||||
Enable the kernel to trace every kernel function. This is done
|
||||
by using a compiler feature to insert a small, 5-byte No-Operation
|
||||
|
|
@ -179,7 +179,7 @@ config TRACE_PREEMPT_TOGGLE
|
|||
config PREEMPTIRQ_EVENTS
|
||||
bool "Enable trace events for preempt and irq disable/enable"
|
||||
select TRACE_IRQFLAGS
|
||||
select TRACE_PREEMPT_TOGGLE if PREEMPT
|
||||
select TRACE_PREEMPT_TOGGLE if PREEMPTION
|
||||
select GENERIC_TRACER
|
||||
default n
|
||||
help
|
||||
|
|
@ -214,7 +214,7 @@ config PREEMPT_TRACER
|
|||
bool "Preemption-off Latency Tracer"
|
||||
default n
|
||||
depends on !ARCH_USES_GETTIMEOFFSET
|
||||
depends on PREEMPT
|
||||
depends on PREEMPTION
|
||||
select GENERIC_TRACER
|
||||
select TRACER_MAX_TRACE
|
||||
select RING_BUFFER_ALLOW_SWAP
|
||||
|
|
@ -520,7 +520,8 @@ config BPF_EVENTS
|
|||
bool
|
||||
default y
|
||||
help
|
||||
This allows the user to attach BPF programs to kprobe events.
|
||||
This allows the user to attach BPF programs to kprobe, uprobe, and
|
||||
tracepoint events.
|
||||
|
||||
config DYNAMIC_EVENTS
|
||||
def_bool n
|
||||
|
|
|
|||
|
|
@ -2814,7 +2814,7 @@ int ftrace_shutdown(struct ftrace_ops *ops, int command)
|
|||
* synchornize_rcu_tasks() will wait for those tasks to
|
||||
* execute and either schedule voluntarily or enter user space.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_PREEMPT))
|
||||
if (IS_ENABLED(CONFIG_PREEMPTION))
|
||||
synchronize_rcu_tasks();
|
||||
|
||||
free_ops:
|
||||
|
|
|
|||
|
|
@ -6,22 +6,22 @@
|
|||
|
||||
/*
|
||||
* Traverse the ftrace_global_list, invoking all entries. The reason that we
|
||||
* can use rcu_dereference_raw_notrace() is that elements removed from this list
|
||||
* can use rcu_dereference_raw_check() is that elements removed from this list
|
||||
* are simply leaked, so there is no need to interact with a grace-period
|
||||
* mechanism. The rcu_dereference_raw_notrace() calls are needed to handle
|
||||
* mechanism. The rcu_dereference_raw_check() calls are needed to handle
|
||||
* concurrent insertions into the ftrace_global_list.
|
||||
*
|
||||
* Silly Alpha and silly pointer-speculation compiler optimizations!
|
||||
*/
|
||||
#define do_for_each_ftrace_op(op, list) \
|
||||
op = rcu_dereference_raw_notrace(list); \
|
||||
op = rcu_dereference_raw_check(list); \
|
||||
do
|
||||
|
||||
/*
|
||||
* Optimized for just a single item in the list (as that is the normal case).
|
||||
*/
|
||||
#define while_for_each_ftrace_op(op) \
|
||||
while (likely(op = rcu_dereference_raw_notrace((op)->next)) && \
|
||||
while (likely(op = rcu_dereference_raw_check((op)->next)) && \
|
||||
unlikely((op) != &ftrace_list_end))
|
||||
|
||||
extern struct ftrace_ops __rcu *ftrace_ops_list;
|
||||
|
|
|
|||
|
|
@ -267,7 +267,7 @@ static void ring_buffer_producer(void)
|
|||
if (consumer && !(cnt % wakeup_interval))
|
||||
wake_up_process(consumer);
|
||||
|
||||
#ifndef CONFIG_PREEMPT
|
||||
#ifndef CONFIG_PREEMPTION
|
||||
/*
|
||||
* If we are a non preempt kernel, the 10 second run will
|
||||
* stop everything while it runs. Instead, we will call
|
||||
|
|
|
|||
|
|
@ -2642,10 +2642,10 @@ static void ftrace_exports(struct ring_buffer_event *event)
|
|||
|
||||
preempt_disable_notrace();
|
||||
|
||||
export = rcu_dereference_raw_notrace(ftrace_exports_list);
|
||||
export = rcu_dereference_raw_check(ftrace_exports_list);
|
||||
while (export) {
|
||||
trace_process_export(export, event);
|
||||
export = rcu_dereference_raw_notrace(export->next);
|
||||
export = rcu_dereference_raw_check(export->next);
|
||||
}
|
||||
|
||||
preempt_enable_notrace();
|
||||
|
|
|
|||
|
|
@ -255,12 +255,12 @@ void *trace_event_buffer_reserve(struct trace_event_buffer *fbuffer,
|
|||
local_save_flags(fbuffer->flags);
|
||||
fbuffer->pc = preempt_count();
|
||||
/*
|
||||
* If CONFIG_PREEMPT is enabled, then the tracepoint itself disables
|
||||
* If CONFIG_PREEMPTION is enabled, then the tracepoint itself disables
|
||||
* preemption (adding one to the preempt_count). Since we are
|
||||
* interested in the preempt_count at the time the tracepoint was
|
||||
* hit, we need to subtract one to offset the increment.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_PREEMPT))
|
||||
if (IS_ENABLED(CONFIG_PREEMPTION))
|
||||
fbuffer->pc--;
|
||||
fbuffer->trace_file = trace_file;
|
||||
|
||||
|
|
|
|||
|
|
@ -579,8 +579,7 @@ probe_wakeup(void *ignore, struct task_struct *p)
|
|||
else
|
||||
tracing_dl = 0;
|
||||
|
||||
wakeup_task = p;
|
||||
get_task_struct(wakeup_task);
|
||||
wakeup_task = get_task_struct(p);
|
||||
|
||||
local_save_flags(flags);
|
||||
|
||||
|
|
|
|||
|
|
@ -490,10 +490,10 @@ static void watchdog_enable(unsigned int cpu)
|
|||
* Start the timer first to prevent the NMI watchdog triggering
|
||||
* before the timer has a chance to fire.
|
||||
*/
|
||||
hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
||||
hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_HARD);
|
||||
hrtimer->function = watchdog_timer_fn;
|
||||
hrtimer_start(hrtimer, ns_to_ktime(sample_period),
|
||||
HRTIMER_MODE_REL_PINNED);
|
||||
HRTIMER_MODE_REL_PINNED_HARD);
|
||||
|
||||
/* Initialize timestamp */
|
||||
__touch_watchdog();
|
||||
|
|
|
|||
|
|
@ -3329,7 +3329,7 @@ EXPORT_SYMBOL_GPL(execute_in_process_context);
|
|||
*
|
||||
* Undo alloc_workqueue_attrs().
|
||||
*/
|
||||
static void free_workqueue_attrs(struct workqueue_attrs *attrs)
|
||||
void free_workqueue_attrs(struct workqueue_attrs *attrs)
|
||||
{
|
||||
if (attrs) {
|
||||
free_cpumask_var(attrs->cpumask);
|
||||
|
|
@ -3345,7 +3345,7 @@ static void free_workqueue_attrs(struct workqueue_attrs *attrs)
|
|||
*
|
||||
* Return: The allocated new workqueue_attr on success. %NULL on failure.
|
||||
*/
|
||||
static struct workqueue_attrs *alloc_workqueue_attrs(void)
|
||||
struct workqueue_attrs *alloc_workqueue_attrs(void)
|
||||
{
|
||||
struct workqueue_attrs *attrs;
|
||||
|
||||
|
|
@ -4030,16 +4030,20 @@ static int apply_workqueue_attrs_locked(struct workqueue_struct *wq,
|
|||
*
|
||||
* Performs GFP_KERNEL allocations.
|
||||
*
|
||||
* Assumes caller has CPU hotplug read exclusion, i.e. get_online_cpus().
|
||||
*
|
||||
* Return: 0 on success and -errno on failure.
|
||||
*/
|
||||
static int apply_workqueue_attrs(struct workqueue_struct *wq,
|
||||
int apply_workqueue_attrs(struct workqueue_struct *wq,
|
||||
const struct workqueue_attrs *attrs)
|
||||
{
|
||||
int ret;
|
||||
|
||||
apply_wqattrs_lock();
|
||||
lockdep_assert_cpus_held();
|
||||
|
||||
mutex_lock(&wq_pool_mutex);
|
||||
ret = apply_workqueue_attrs_locked(wq, attrs);
|
||||
apply_wqattrs_unlock();
|
||||
mutex_unlock(&wq_pool_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -4152,16 +4156,21 @@ static int alloc_and_link_pwqs(struct workqueue_struct *wq)
|
|||
mutex_unlock(&wq->mutex);
|
||||
}
|
||||
return 0;
|
||||
} else if (wq->flags & __WQ_ORDERED) {
|
||||
}
|
||||
|
||||
get_online_cpus();
|
||||
if (wq->flags & __WQ_ORDERED) {
|
||||
ret = apply_workqueue_attrs(wq, ordered_wq_attrs[highpri]);
|
||||
/* there should only be single pwq for ordering guarantee */
|
||||
WARN(!ret && (wq->pwqs.next != &wq->dfl_pwq->pwqs_node ||
|
||||
wq->pwqs.prev != &wq->dfl_pwq->pwqs_node),
|
||||
"ordering guarantee broken for workqueue %s\n", wq->name);
|
||||
return ret;
|
||||
} else {
|
||||
return apply_workqueue_attrs(wq, unbound_std_wq_attrs[highpri]);
|
||||
ret = apply_workqueue_attrs(wq, unbound_std_wq_attrs[highpri]);
|
||||
}
|
||||
put_online_cpus();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int wq_clamp_max_active(int max_active, unsigned int flags,
|
||||
|
|
|
|||
Loading…
Reference in a new issue