Merge branch 'core/urgent' into core/entry
Pick up the entry fix before further modifications.
This commit is contained in:
+4
-4
@@ -12,7 +12,7 @@ obj-y = fork.o exec_domain.o panic.o \
|
||||
notifier.o ksysfs.o cred.o reboot.o \
|
||||
async.o range.o smpboot.o ucount.o regset.o
|
||||
|
||||
obj-$(CONFIG_BPFILTER) += usermode_driver.o
|
||||
obj-$(CONFIG_USERMODE_DRIVER) += usermode_driver.o
|
||||
obj-$(CONFIG_MODULES) += kmod.o
|
||||
obj-$(CONFIG_MULTIUSER) += groups.o
|
||||
|
||||
@@ -38,9 +38,6 @@ KASAN_SANITIZE_kcov.o := n
|
||||
KCSAN_SANITIZE_kcov.o := n
|
||||
CFLAGS_kcov.o := $(call cc-option, -fno-conserve-stack) -fno-stack-protector
|
||||
|
||||
# cond_syscall is currently not LTO compatible
|
||||
CFLAGS_sys_ni.o = $(DISABLE_LTO)
|
||||
|
||||
obj-y += sched/
|
||||
obj-y += locking/
|
||||
obj-y += power/
|
||||
@@ -111,6 +108,7 @@ obj-$(CONFIG_CPU_PM) += cpu_pm.o
|
||||
obj-$(CONFIG_BPF) += bpf/
|
||||
obj-$(CONFIG_KCSAN) += kcsan/
|
||||
obj-$(CONFIG_SHADOW_CALL_STACK) += scs.o
|
||||
obj-$(CONFIG_HAVE_STATIC_CALL_INLINE) += static_call.o
|
||||
|
||||
obj-$(CONFIG_PERF_EVENTS) += events/
|
||||
|
||||
@@ -133,6 +131,8 @@ KASAN_SANITIZE_stackleak.o := n
|
||||
KCSAN_SANITIZE_stackleak.o := n
|
||||
KCOV_INSTRUMENT_stackleak.o := n
|
||||
|
||||
obj-$(CONFIG_SCF_TORTURE_TEST) += scftorture.o
|
||||
|
||||
$(obj)/configs.o: $(obj)/config_data.gz
|
||||
|
||||
targets += config_data.gz
|
||||
|
||||
+4
-6
@@ -25,7 +25,7 @@
|
||||
* Now we silently close acct_file on attempt to reopen. Cleaned sys_acct().
|
||||
* XTerms and EMACS are manifestations of pure evil. 21/10/98, AV.
|
||||
*
|
||||
* Fixed a nasty interaction with with sys_umount(). If the accointing
|
||||
* Fixed a nasty interaction with sys_umount(). If the accounting
|
||||
* was suspeneded we failed to stop it on umount(). Messy.
|
||||
* Another one: remount to readonly didn't stop accounting.
|
||||
* Question: what should we do if we have CAP_SYS_ADMIN but not
|
||||
@@ -263,12 +263,12 @@ static DEFINE_MUTEX(acct_on_mutex);
|
||||
* sys_acct - enable/disable process accounting
|
||||
* @name: file name for accounting records or NULL to shutdown accounting
|
||||
*
|
||||
* Returns 0 for success or negative errno values for failure.
|
||||
*
|
||||
* sys_acct() is the only system call needed to implement process
|
||||
* accounting. It takes the name of the file where accounting records
|
||||
* should be written. If the filename is NULL, accounting will be
|
||||
* shutdown.
|
||||
*
|
||||
* Returns: 0 for success or negative errno values for failure.
|
||||
*/
|
||||
SYSCALL_DEFINE1(acct, const char __user *, name)
|
||||
{
|
||||
@@ -586,9 +586,7 @@ static void slow_acct_process(struct pid_namespace *ns)
|
||||
}
|
||||
|
||||
/**
|
||||
* acct_process
|
||||
*
|
||||
* handles process accounting for an exiting task
|
||||
* acct_process - handles process accounting for an exiting task
|
||||
*/
|
||||
void acct_process(void)
|
||||
{
|
||||
|
||||
+4
-5
@@ -123,9 +123,9 @@ static u32 audit_backlog_limit = 64;
|
||||
static u32 audit_backlog_wait_time = AUDIT_BACKLOG_WAIT_TIME;
|
||||
|
||||
/* The identity of the user shutting down the audit system. */
|
||||
kuid_t audit_sig_uid = INVALID_UID;
|
||||
pid_t audit_sig_pid = -1;
|
||||
u32 audit_sig_sid = 0;
|
||||
static kuid_t audit_sig_uid = INVALID_UID;
|
||||
static pid_t audit_sig_pid = -1;
|
||||
static u32 audit_sig_sid;
|
||||
|
||||
/* Records can be lost in several ways:
|
||||
0) [suppressed in audit_alloc]
|
||||
@@ -934,8 +934,7 @@ static void audit_free_reply(struct audit_reply *reply)
|
||||
if (!reply)
|
||||
return;
|
||||
|
||||
if (reply->skb)
|
||||
kfree_skb(reply->skb);
|
||||
kfree_skb(reply->skb);
|
||||
if (reply->net)
|
||||
put_net(reply->net);
|
||||
kfree(reply);
|
||||
|
||||
@@ -327,10 +327,6 @@ static inline int audit_signal_info_syscall(struct task_struct *t)
|
||||
|
||||
extern char *audit_unpack_string(void **bufp, size_t *remain, size_t len);
|
||||
|
||||
extern pid_t audit_sig_pid;
|
||||
extern kuid_t audit_sig_uid;
|
||||
extern u32 audit_sig_sid;
|
||||
|
||||
extern int audit_filter(int msgtype, unsigned int listtype);
|
||||
|
||||
extern void audit_ctl_lock(void);
|
||||
|
||||
@@ -681,7 +681,7 @@ static struct audit_rule_data *audit_krule_to_data(struct audit_krule *krule)
|
||||
data->values[i] = AUDIT_UID_UNSET;
|
||||
break;
|
||||
}
|
||||
/* fall through - if set */
|
||||
fallthrough; /* if set */
|
||||
default:
|
||||
data->values[i] = f->val;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ CFLAGS_core.o += $(call cc-disable-warning, override-init)
|
||||
obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o tnum.o bpf_iter.o map_iter.o task_iter.o prog_iter.o
|
||||
obj-$(CONFIG_BPF_SYSCALL) += hashtab.o arraymap.o percpu_freelist.o bpf_lru_list.o lpm_trie.o map_in_map.o
|
||||
obj-$(CONFIG_BPF_SYSCALL) += local_storage.o queue_stack_maps.o ringbuf.o
|
||||
obj-${CONFIG_BPF_LSM} += bpf_inode_storage.o
|
||||
obj-$(CONFIG_BPF_SYSCALL) += disasm.o
|
||||
obj-$(CONFIG_BPF_JIT) += trampoline.o
|
||||
obj-$(CONFIG_BPF_SYSCALL) += btf.o
|
||||
@@ -12,6 +13,7 @@ obj-$(CONFIG_BPF_JIT) += dispatcher.o
|
||||
ifeq ($(CONFIG_NET),y)
|
||||
obj-$(CONFIG_BPF_SYSCALL) += devmap.o
|
||||
obj-$(CONFIG_BPF_SYSCALL) += cpumap.o
|
||||
obj-$(CONFIG_BPF_SYSCALL) += bpf_local_storage.o
|
||||
obj-$(CONFIG_BPF_SYSCALL) += offload.o
|
||||
obj-$(CONFIG_BPF_SYSCALL) += net_namespace.o
|
||||
endif
|
||||
@@ -29,3 +31,4 @@ ifeq ($(CONFIG_BPF_JIT),y)
|
||||
obj-$(CONFIG_BPF_SYSCALL) += bpf_struct_ops.o
|
||||
obj-${CONFIG_BPF_LSM} += bpf_lsm.o
|
||||
endif
|
||||
obj-$(CONFIG_BPF_PRELOAD) += preload/
|
||||
|
||||
+84
-18
@@ -10,11 +10,13 @@
|
||||
#include <linux/filter.h>
|
||||
#include <linux/perf_event.h>
|
||||
#include <uapi/linux/btf.h>
|
||||
#include <linux/rcupdate_trace.h>
|
||||
|
||||
#include "map_in_map.h"
|
||||
|
||||
#define ARRAY_CREATE_FLAG_MASK \
|
||||
(BPF_F_NUMA_NODE | BPF_F_MMAPABLE | BPF_F_ACCESS_MASK)
|
||||
(BPF_F_NUMA_NODE | BPF_F_MMAPABLE | BPF_F_ACCESS_MASK | \
|
||||
BPF_F_PRESERVE_ELEMS | BPF_F_INNER_MAP)
|
||||
|
||||
static void bpf_array_free_percpu(struct bpf_array *array)
|
||||
{
|
||||
@@ -60,7 +62,11 @@ int array_map_alloc_check(union bpf_attr *attr)
|
||||
return -EINVAL;
|
||||
|
||||
if (attr->map_type != BPF_MAP_TYPE_ARRAY &&
|
||||
attr->map_flags & BPF_F_MMAPABLE)
|
||||
attr->map_flags & (BPF_F_MMAPABLE | BPF_F_INNER_MAP))
|
||||
return -EINVAL;
|
||||
|
||||
if (attr->map_type != BPF_MAP_TYPE_PERF_EVENT_ARRAY &&
|
||||
attr->map_flags & BPF_F_PRESERVE_ELEMS)
|
||||
return -EINVAL;
|
||||
|
||||
if (attr->value_size > KMALLOC_MAX_SIZE)
|
||||
@@ -208,7 +214,7 @@ static int array_map_direct_value_meta(const struct bpf_map *map, u64 imm,
|
||||
}
|
||||
|
||||
/* emit BPF instructions equivalent to C code of array_map_lookup_elem() */
|
||||
static u32 array_map_gen_lookup(struct bpf_map *map, struct bpf_insn *insn_buf)
|
||||
static int array_map_gen_lookup(struct bpf_map *map, struct bpf_insn *insn_buf)
|
||||
{
|
||||
struct bpf_array *array = container_of(map, struct bpf_array, map);
|
||||
struct bpf_insn *insn = insn_buf;
|
||||
@@ -217,6 +223,9 @@ static u32 array_map_gen_lookup(struct bpf_map *map, struct bpf_insn *insn_buf)
|
||||
const int map_ptr = BPF_REG_1;
|
||||
const int index = BPF_REG_2;
|
||||
|
||||
if (map->map_flags & BPF_F_INNER_MAP)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
*insn++ = BPF_ALU64_IMM(BPF_ADD, map_ptr, offsetof(struct bpf_array, value));
|
||||
*insn++ = BPF_LDX_MEM(BPF_W, ret, index, 0);
|
||||
if (!map->bypass_spec_v1) {
|
||||
@@ -487,6 +496,15 @@ static int array_map_mmap(struct bpf_map *map, struct vm_area_struct *vma)
|
||||
vma->vm_pgoff + pgoff);
|
||||
}
|
||||
|
||||
static bool array_map_meta_equal(const struct bpf_map *meta0,
|
||||
const struct bpf_map *meta1)
|
||||
{
|
||||
if (!bpf_map_meta_equal(meta0, meta1))
|
||||
return false;
|
||||
return meta0->map_flags & BPF_F_INNER_MAP ? true :
|
||||
meta0->max_entries == meta1->max_entries;
|
||||
}
|
||||
|
||||
struct bpf_iter_seq_array_map_info {
|
||||
struct bpf_map *map;
|
||||
void *percpu_value_buf;
|
||||
@@ -625,6 +643,7 @@ static const struct bpf_iter_seq_info iter_seq_info = {
|
||||
|
||||
static int array_map_btf_id;
|
||||
const struct bpf_map_ops array_map_ops = {
|
||||
.map_meta_equal = array_map_meta_equal,
|
||||
.map_alloc_check = array_map_alloc_check,
|
||||
.map_alloc = array_map_alloc,
|
||||
.map_free = array_map_free,
|
||||
@@ -647,6 +666,7 @@ const struct bpf_map_ops array_map_ops = {
|
||||
|
||||
static int percpu_array_map_btf_id;
|
||||
const struct bpf_map_ops percpu_array_map_ops = {
|
||||
.map_meta_equal = bpf_map_meta_equal,
|
||||
.map_alloc_check = array_map_alloc_check,
|
||||
.map_alloc = array_map_alloc,
|
||||
.map_free = array_map_free,
|
||||
@@ -888,6 +908,7 @@ static void prog_array_map_poke_run(struct bpf_map *map, u32 key,
|
||||
struct bpf_prog *old,
|
||||
struct bpf_prog *new)
|
||||
{
|
||||
u8 *old_addr, *new_addr, *old_bypass_addr;
|
||||
struct prog_poke_elem *elem;
|
||||
struct bpf_array_aux *aux;
|
||||
|
||||
@@ -908,12 +929,13 @@ static void prog_array_map_poke_run(struct bpf_map *map, u32 key,
|
||||
* there could be danger of use after free otherwise.
|
||||
* 2) Initially when we start tracking aux, the program
|
||||
* is not JITed yet and also does not have a kallsyms
|
||||
* entry. We skip these as poke->ip_stable is not
|
||||
* active yet. The JIT will do the final fixup before
|
||||
* setting it stable. The various poke->ip_stable are
|
||||
* successively activated, so tail call updates can
|
||||
* arrive from here while JIT is still finishing its
|
||||
* final fixup for non-activated poke entries.
|
||||
* entry. We skip these as poke->tailcall_target_stable
|
||||
* is not active yet. The JIT will do the final fixup
|
||||
* before setting it stable. The various
|
||||
* poke->tailcall_target_stable are successively
|
||||
* activated, so tail call updates can arrive from here
|
||||
* while JIT is still finishing its final fixup for
|
||||
* non-activated poke entries.
|
||||
* 3) On program teardown, the program's kallsym entry gets
|
||||
* removed out of RCU callback, but we can only untrack
|
||||
* from sleepable context, therefore bpf_arch_text_poke()
|
||||
@@ -930,7 +952,7 @@ static void prog_array_map_poke_run(struct bpf_map *map, u32 key,
|
||||
* 5) Any other error happening below from bpf_arch_text_poke()
|
||||
* is a unexpected bug.
|
||||
*/
|
||||
if (!READ_ONCE(poke->ip_stable))
|
||||
if (!READ_ONCE(poke->tailcall_target_stable))
|
||||
continue;
|
||||
if (poke->reason != BPF_POKE_REASON_TAIL_CALL)
|
||||
continue;
|
||||
@@ -938,12 +960,39 @@ static void prog_array_map_poke_run(struct bpf_map *map, u32 key,
|
||||
poke->tail_call.key != key)
|
||||
continue;
|
||||
|
||||
ret = bpf_arch_text_poke(poke->ip, BPF_MOD_JUMP,
|
||||
old ? (u8 *)old->bpf_func +
|
||||
poke->adj_off : NULL,
|
||||
new ? (u8 *)new->bpf_func +
|
||||
poke->adj_off : NULL);
|
||||
BUG_ON(ret < 0 && ret != -EINVAL);
|
||||
old_bypass_addr = old ? NULL : poke->bypass_addr;
|
||||
old_addr = old ? (u8 *)old->bpf_func + poke->adj_off : NULL;
|
||||
new_addr = new ? (u8 *)new->bpf_func + poke->adj_off : NULL;
|
||||
|
||||
if (new) {
|
||||
ret = bpf_arch_text_poke(poke->tailcall_target,
|
||||
BPF_MOD_JUMP,
|
||||
old_addr, new_addr);
|
||||
BUG_ON(ret < 0 && ret != -EINVAL);
|
||||
if (!old) {
|
||||
ret = bpf_arch_text_poke(poke->tailcall_bypass,
|
||||
BPF_MOD_JUMP,
|
||||
poke->bypass_addr,
|
||||
NULL);
|
||||
BUG_ON(ret < 0 && ret != -EINVAL);
|
||||
}
|
||||
} else {
|
||||
ret = bpf_arch_text_poke(poke->tailcall_bypass,
|
||||
BPF_MOD_JUMP,
|
||||
old_bypass_addr,
|
||||
poke->bypass_addr);
|
||||
BUG_ON(ret < 0 && ret != -EINVAL);
|
||||
/* let other CPUs finish the execution of program
|
||||
* so that it will not possible to expose them
|
||||
* to invalid nop, stack unwind, nop state
|
||||
*/
|
||||
if (!ret)
|
||||
synchronize_rcu();
|
||||
ret = bpf_arch_text_poke(poke->tailcall_target,
|
||||
BPF_MOD_JUMP,
|
||||
old_addr, NULL);
|
||||
BUG_ON(ret < 0 && ret != -EINVAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1003,6 +1052,11 @@ static void prog_array_map_free(struct bpf_map *map)
|
||||
fd_array_map_free(map);
|
||||
}
|
||||
|
||||
/* prog_array->aux->{type,jited} is a runtime binding.
|
||||
* Doing static check alone in the verifier is not enough.
|
||||
* Thus, prog_array_map cannot be used as an inner_map
|
||||
* and map_meta_equal is not implemented.
|
||||
*/
|
||||
static int prog_array_map_btf_id;
|
||||
const struct bpf_map_ops prog_array_map_ops = {
|
||||
.map_alloc_check = fd_array_map_alloc_check,
|
||||
@@ -1090,6 +1144,9 @@ static void perf_event_fd_array_release(struct bpf_map *map,
|
||||
struct bpf_event_entry *ee;
|
||||
int i;
|
||||
|
||||
if (map->map_flags & BPF_F_PRESERVE_ELEMS)
|
||||
return;
|
||||
|
||||
rcu_read_lock();
|
||||
for (i = 0; i < array->map.max_entries; i++) {
|
||||
ee = READ_ONCE(array->ptrs[i]);
|
||||
@@ -1099,11 +1156,19 @@ static void perf_event_fd_array_release(struct bpf_map *map,
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
static void perf_event_fd_array_map_free(struct bpf_map *map)
|
||||
{
|
||||
if (map->map_flags & BPF_F_PRESERVE_ELEMS)
|
||||
bpf_fd_array_map_clear(map);
|
||||
fd_array_map_free(map);
|
||||
}
|
||||
|
||||
static int perf_event_array_map_btf_id;
|
||||
const struct bpf_map_ops perf_event_array_map_ops = {
|
||||
.map_meta_equal = bpf_map_meta_equal,
|
||||
.map_alloc_check = fd_array_map_alloc_check,
|
||||
.map_alloc = array_map_alloc,
|
||||
.map_free = fd_array_map_free,
|
||||
.map_free = perf_event_fd_array_map_free,
|
||||
.map_get_next_key = array_map_get_next_key,
|
||||
.map_lookup_elem = fd_array_map_lookup_elem,
|
||||
.map_delete_elem = fd_array_map_delete_elem,
|
||||
@@ -1137,6 +1202,7 @@ static void cgroup_fd_array_free(struct bpf_map *map)
|
||||
|
||||
static int cgroup_array_map_btf_id;
|
||||
const struct bpf_map_ops cgroup_array_map_ops = {
|
||||
.map_meta_equal = bpf_map_meta_equal,
|
||||
.map_alloc_check = fd_array_map_alloc_check,
|
||||
.map_alloc = array_map_alloc,
|
||||
.map_free = cgroup_fd_array_free,
|
||||
@@ -1190,7 +1256,7 @@ static void *array_of_map_lookup_elem(struct bpf_map *map, void *key)
|
||||
return READ_ONCE(*inner_map);
|
||||
}
|
||||
|
||||
static u32 array_of_map_gen_lookup(struct bpf_map *map,
|
||||
static int array_of_map_gen_lookup(struct bpf_map *map,
|
||||
struct bpf_insn *insn_buf)
|
||||
{
|
||||
struct bpf_array *array = container_of(map, struct bpf_array, map);
|
||||
|
||||
@@ -0,0 +1,272 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2019 Facebook
|
||||
* Copyright 2020 Google LLC.
|
||||
*/
|
||||
|
||||
#include <linux/rculist.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/hash.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/bpf_local_storage.h>
|
||||
#include <net/sock.h>
|
||||
#include <uapi/linux/sock_diag.h>
|
||||
#include <uapi/linux/btf.h>
|
||||
#include <linux/bpf_lsm.h>
|
||||
#include <linux/btf_ids.h>
|
||||
#include <linux/fdtable.h>
|
||||
|
||||
DEFINE_BPF_STORAGE_CACHE(inode_cache);
|
||||
|
||||
static struct bpf_local_storage __rcu **
|
||||
inode_storage_ptr(void *owner)
|
||||
{
|
||||
struct inode *inode = owner;
|
||||
struct bpf_storage_blob *bsb;
|
||||
|
||||
bsb = bpf_inode(inode);
|
||||
if (!bsb)
|
||||
return NULL;
|
||||
return &bsb->storage;
|
||||
}
|
||||
|
||||
static struct bpf_local_storage_data *inode_storage_lookup(struct inode *inode,
|
||||
struct bpf_map *map,
|
||||
bool cacheit_lockit)
|
||||
{
|
||||
struct bpf_local_storage *inode_storage;
|
||||
struct bpf_local_storage_map *smap;
|
||||
struct bpf_storage_blob *bsb;
|
||||
|
||||
bsb = bpf_inode(inode);
|
||||
if (!bsb)
|
||||
return NULL;
|
||||
|
||||
inode_storage = rcu_dereference(bsb->storage);
|
||||
if (!inode_storage)
|
||||
return NULL;
|
||||
|
||||
smap = (struct bpf_local_storage_map *)map;
|
||||
return bpf_local_storage_lookup(inode_storage, smap, cacheit_lockit);
|
||||
}
|
||||
|
||||
void bpf_inode_storage_free(struct inode *inode)
|
||||
{
|
||||
struct bpf_local_storage_elem *selem;
|
||||
struct bpf_local_storage *local_storage;
|
||||
bool free_inode_storage = false;
|
||||
struct bpf_storage_blob *bsb;
|
||||
struct hlist_node *n;
|
||||
|
||||
bsb = bpf_inode(inode);
|
||||
if (!bsb)
|
||||
return;
|
||||
|
||||
rcu_read_lock();
|
||||
|
||||
local_storage = rcu_dereference(bsb->storage);
|
||||
if (!local_storage) {
|
||||
rcu_read_unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Netiher the bpf_prog nor the bpf-map's syscall
|
||||
* could be modifying the local_storage->list now.
|
||||
* Thus, no elem can be added-to or deleted-from the
|
||||
* local_storage->list by the bpf_prog or by the bpf-map's syscall.
|
||||
*
|
||||
* It is racing with bpf_local_storage_map_free() alone
|
||||
* when unlinking elem from the local_storage->list and
|
||||
* the map's bucket->list.
|
||||
*/
|
||||
raw_spin_lock_bh(&local_storage->lock);
|
||||
hlist_for_each_entry_safe(selem, n, &local_storage->list, snode) {
|
||||
/* Always unlink from map before unlinking from
|
||||
* local_storage.
|
||||
*/
|
||||
bpf_selem_unlink_map(selem);
|
||||
free_inode_storage = bpf_selem_unlink_storage_nolock(
|
||||
local_storage, selem, false);
|
||||
}
|
||||
raw_spin_unlock_bh(&local_storage->lock);
|
||||
rcu_read_unlock();
|
||||
|
||||
/* free_inoode_storage should always be true as long as
|
||||
* local_storage->list was non-empty.
|
||||
*/
|
||||
if (free_inode_storage)
|
||||
kfree_rcu(local_storage, rcu);
|
||||
}
|
||||
|
||||
static void *bpf_fd_inode_storage_lookup_elem(struct bpf_map *map, void *key)
|
||||
{
|
||||
struct bpf_local_storage_data *sdata;
|
||||
struct file *f;
|
||||
int fd;
|
||||
|
||||
fd = *(int *)key;
|
||||
f = fget_raw(fd);
|
||||
if (!f)
|
||||
return NULL;
|
||||
|
||||
sdata = inode_storage_lookup(f->f_inode, map, true);
|
||||
fput(f);
|
||||
return sdata ? sdata->data : NULL;
|
||||
}
|
||||
|
||||
static int bpf_fd_inode_storage_update_elem(struct bpf_map *map, void *key,
|
||||
void *value, u64 map_flags)
|
||||
{
|
||||
struct bpf_local_storage_data *sdata;
|
||||
struct file *f;
|
||||
int fd;
|
||||
|
||||
fd = *(int *)key;
|
||||
f = fget_raw(fd);
|
||||
if (!f || !inode_storage_ptr(f->f_inode))
|
||||
return -EBADF;
|
||||
|
||||
sdata = bpf_local_storage_update(f->f_inode,
|
||||
(struct bpf_local_storage_map *)map,
|
||||
value, map_flags);
|
||||
fput(f);
|
||||
return PTR_ERR_OR_ZERO(sdata);
|
||||
}
|
||||
|
||||
static int inode_storage_delete(struct inode *inode, struct bpf_map *map)
|
||||
{
|
||||
struct bpf_local_storage_data *sdata;
|
||||
|
||||
sdata = inode_storage_lookup(inode, map, false);
|
||||
if (!sdata)
|
||||
return -ENOENT;
|
||||
|
||||
bpf_selem_unlink(SELEM(sdata));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bpf_fd_inode_storage_delete_elem(struct bpf_map *map, void *key)
|
||||
{
|
||||
struct file *f;
|
||||
int fd, err;
|
||||
|
||||
fd = *(int *)key;
|
||||
f = fget_raw(fd);
|
||||
if (!f)
|
||||
return -EBADF;
|
||||
|
||||
err = inode_storage_delete(f->f_inode, map);
|
||||
fput(f);
|
||||
return err;
|
||||
}
|
||||
|
||||
BPF_CALL_4(bpf_inode_storage_get, struct bpf_map *, map, struct inode *, inode,
|
||||
void *, value, u64, flags)
|
||||
{
|
||||
struct bpf_local_storage_data *sdata;
|
||||
|
||||
if (flags & ~(BPF_LOCAL_STORAGE_GET_F_CREATE))
|
||||
return (unsigned long)NULL;
|
||||
|
||||
/* explicitly check that the inode_storage_ptr is not
|
||||
* NULL as inode_storage_lookup returns NULL in this case and
|
||||
* bpf_local_storage_update expects the owner to have a
|
||||
* valid storage pointer.
|
||||
*/
|
||||
if (!inode_storage_ptr(inode))
|
||||
return (unsigned long)NULL;
|
||||
|
||||
sdata = inode_storage_lookup(inode, map, true);
|
||||
if (sdata)
|
||||
return (unsigned long)sdata->data;
|
||||
|
||||
/* This helper must only called from where the inode is gurranteed
|
||||
* to have a refcount and cannot be freed.
|
||||
*/
|
||||
if (flags & BPF_LOCAL_STORAGE_GET_F_CREATE) {
|
||||
sdata = bpf_local_storage_update(
|
||||
inode, (struct bpf_local_storage_map *)map, value,
|
||||
BPF_NOEXIST);
|
||||
return IS_ERR(sdata) ? (unsigned long)NULL :
|
||||
(unsigned long)sdata->data;
|
||||
}
|
||||
|
||||
return (unsigned long)NULL;
|
||||
}
|
||||
|
||||
BPF_CALL_2(bpf_inode_storage_delete,
|
||||
struct bpf_map *, map, struct inode *, inode)
|
||||
{
|
||||
/* This helper must only called from where the inode is gurranteed
|
||||
* to have a refcount and cannot be freed.
|
||||
*/
|
||||
return inode_storage_delete(inode, map);
|
||||
}
|
||||
|
||||
static int notsupp_get_next_key(struct bpf_map *map, void *key,
|
||||
void *next_key)
|
||||
{
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
static struct bpf_map *inode_storage_map_alloc(union bpf_attr *attr)
|
||||
{
|
||||
struct bpf_local_storage_map *smap;
|
||||
|
||||
smap = bpf_local_storage_map_alloc(attr);
|
||||
if (IS_ERR(smap))
|
||||
return ERR_CAST(smap);
|
||||
|
||||
smap->cache_idx = bpf_local_storage_cache_idx_get(&inode_cache);
|
||||
return &smap->map;
|
||||
}
|
||||
|
||||
static void inode_storage_map_free(struct bpf_map *map)
|
||||
{
|
||||
struct bpf_local_storage_map *smap;
|
||||
|
||||
smap = (struct bpf_local_storage_map *)map;
|
||||
bpf_local_storage_cache_idx_free(&inode_cache, smap->cache_idx);
|
||||
bpf_local_storage_map_free(smap);
|
||||
}
|
||||
|
||||
static int inode_storage_map_btf_id;
|
||||
const struct bpf_map_ops inode_storage_map_ops = {
|
||||
.map_meta_equal = bpf_map_meta_equal,
|
||||
.map_alloc_check = bpf_local_storage_map_alloc_check,
|
||||
.map_alloc = inode_storage_map_alloc,
|
||||
.map_free = inode_storage_map_free,
|
||||
.map_get_next_key = notsupp_get_next_key,
|
||||
.map_lookup_elem = bpf_fd_inode_storage_lookup_elem,
|
||||
.map_update_elem = bpf_fd_inode_storage_update_elem,
|
||||
.map_delete_elem = bpf_fd_inode_storage_delete_elem,
|
||||
.map_check_btf = bpf_local_storage_map_check_btf,
|
||||
.map_btf_name = "bpf_local_storage_map",
|
||||
.map_btf_id = &inode_storage_map_btf_id,
|
||||
.map_owner_storage_ptr = inode_storage_ptr,
|
||||
};
|
||||
|
||||
BTF_ID_LIST_SINGLE(bpf_inode_storage_btf_ids, struct, inode)
|
||||
|
||||
const struct bpf_func_proto bpf_inode_storage_get_proto = {
|
||||
.func = bpf_inode_storage_get,
|
||||
.gpl_only = false,
|
||||
.ret_type = RET_PTR_TO_MAP_VALUE_OR_NULL,
|
||||
.arg1_type = ARG_CONST_MAP_PTR,
|
||||
.arg2_type = ARG_PTR_TO_BTF_ID,
|
||||
.arg2_btf_id = &bpf_inode_storage_btf_ids[0],
|
||||
.arg3_type = ARG_PTR_TO_MAP_VALUE_OR_NULL,
|
||||
.arg4_type = ARG_ANYTHING,
|
||||
};
|
||||
|
||||
const struct bpf_func_proto bpf_inode_storage_delete_proto = {
|
||||
.func = bpf_inode_storage_delete,
|
||||
.gpl_only = false,
|
||||
.ret_type = RET_INTEGER,
|
||||
.arg1_type = ARG_CONST_MAP_PTR,
|
||||
.arg2_type = ARG_PTR_TO_BTF_ID,
|
||||
.arg2_btf_id = &bpf_inode_storage_btf_ids[0],
|
||||
};
|
||||
+74
-3
@@ -67,6 +67,9 @@ static void bpf_iter_done_stop(struct seq_file *seq)
|
||||
iter_priv->done_stop = true;
|
||||
}
|
||||
|
||||
/* maximum visited objects before bailing out */
|
||||
#define MAX_ITER_OBJECTS 1000000
|
||||
|
||||
/* bpf_seq_read, a customized and simpler version for bpf iterator.
|
||||
* no_llseek is assumed for this file.
|
||||
* The following are differences from seq_read():
|
||||
@@ -79,14 +82,14 @@ static ssize_t bpf_seq_read(struct file *file, char __user *buf, size_t size,
|
||||
{
|
||||
struct seq_file *seq = file->private_data;
|
||||
size_t n, offs, copied = 0;
|
||||
int err = 0;
|
||||
int err = 0, num_objs = 0;
|
||||
void *p;
|
||||
|
||||
mutex_lock(&seq->lock);
|
||||
|
||||
if (!seq->buf) {
|
||||
seq->size = PAGE_SIZE;
|
||||
seq->buf = kmalloc(seq->size, GFP_KERNEL);
|
||||
seq->size = PAGE_SIZE << 3;
|
||||
seq->buf = kvmalloc(seq->size, GFP_KERNEL);
|
||||
if (!seq->buf) {
|
||||
err = -ENOMEM;
|
||||
goto done;
|
||||
@@ -135,6 +138,7 @@ static ssize_t bpf_seq_read(struct file *file, char __user *buf, size_t size,
|
||||
while (1) {
|
||||
loff_t pos = seq->index;
|
||||
|
||||
num_objs++;
|
||||
offs = seq->count;
|
||||
p = seq->op->next(seq, p, &seq->index);
|
||||
if (pos == seq->index) {
|
||||
@@ -153,6 +157,15 @@ static ssize_t bpf_seq_read(struct file *file, char __user *buf, size_t size,
|
||||
if (seq->count >= size)
|
||||
break;
|
||||
|
||||
if (num_objs >= MAX_ITER_OBJECTS) {
|
||||
if (offs == 0) {
|
||||
err = -EAGAIN;
|
||||
seq->op->stop(seq, p);
|
||||
goto done;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
err = seq->op->show(seq, p);
|
||||
if (err > 0) {
|
||||
bpf_iter_dec_seq_num(seq);
|
||||
@@ -377,10 +390,68 @@ out_unlock:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void bpf_iter_link_show_fdinfo(const struct bpf_link *link,
|
||||
struct seq_file *seq)
|
||||
{
|
||||
struct bpf_iter_link *iter_link =
|
||||
container_of(link, struct bpf_iter_link, link);
|
||||
bpf_iter_show_fdinfo_t show_fdinfo;
|
||||
|
||||
seq_printf(seq,
|
||||
"target_name:\t%s\n",
|
||||
iter_link->tinfo->reg_info->target);
|
||||
|
||||
show_fdinfo = iter_link->tinfo->reg_info->show_fdinfo;
|
||||
if (show_fdinfo)
|
||||
show_fdinfo(&iter_link->aux, seq);
|
||||
}
|
||||
|
||||
static int bpf_iter_link_fill_link_info(const struct bpf_link *link,
|
||||
struct bpf_link_info *info)
|
||||
{
|
||||
struct bpf_iter_link *iter_link =
|
||||
container_of(link, struct bpf_iter_link, link);
|
||||
char __user *ubuf = u64_to_user_ptr(info->iter.target_name);
|
||||
bpf_iter_fill_link_info_t fill_link_info;
|
||||
u32 ulen = info->iter.target_name_len;
|
||||
const char *target_name;
|
||||
u32 target_len;
|
||||
|
||||
if (!ulen ^ !ubuf)
|
||||
return -EINVAL;
|
||||
|
||||
target_name = iter_link->tinfo->reg_info->target;
|
||||
target_len = strlen(target_name);
|
||||
info->iter.target_name_len = target_len + 1;
|
||||
|
||||
if (ubuf) {
|
||||
if (ulen >= target_len + 1) {
|
||||
if (copy_to_user(ubuf, target_name, target_len + 1))
|
||||
return -EFAULT;
|
||||
} else {
|
||||
char zero = '\0';
|
||||
|
||||
if (copy_to_user(ubuf, target_name, ulen - 1))
|
||||
return -EFAULT;
|
||||
if (put_user(zero, ubuf + ulen - 1))
|
||||
return -EFAULT;
|
||||
return -ENOSPC;
|
||||
}
|
||||
}
|
||||
|
||||
fill_link_info = iter_link->tinfo->reg_info->fill_link_info;
|
||||
if (fill_link_info)
|
||||
return fill_link_info(&iter_link->aux, info);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct bpf_link_ops bpf_iter_link_lops = {
|
||||
.release = bpf_iter_link_release,
|
||||
.dealloc = bpf_iter_link_dealloc,
|
||||
.update_prog = bpf_iter_link_replace,
|
||||
.show_fdinfo = bpf_iter_link_show_fdinfo,
|
||||
.fill_link_info = bpf_iter_link_fill_link_info,
|
||||
};
|
||||
|
||||
bool bpf_link_is_iter(struct bpf_link *link)
|
||||
|
||||
@@ -0,0 +1,600 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2019 Facebook */
|
||||
#include <linux/rculist.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/hash.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/btf_ids.h>
|
||||
#include <linux/bpf_local_storage.h>
|
||||
#include <net/sock.h>
|
||||
#include <uapi/linux/sock_diag.h>
|
||||
#include <uapi/linux/btf.h>
|
||||
|
||||
#define BPF_LOCAL_STORAGE_CREATE_FLAG_MASK (BPF_F_NO_PREALLOC | BPF_F_CLONE)
|
||||
|
||||
static struct bpf_local_storage_map_bucket *
|
||||
select_bucket(struct bpf_local_storage_map *smap,
|
||||
struct bpf_local_storage_elem *selem)
|
||||
{
|
||||
return &smap->buckets[hash_ptr(selem, smap->bucket_log)];
|
||||
}
|
||||
|
||||
static int mem_charge(struct bpf_local_storage_map *smap, void *owner, u32 size)
|
||||
{
|
||||
struct bpf_map *map = &smap->map;
|
||||
|
||||
if (!map->ops->map_local_storage_charge)
|
||||
return 0;
|
||||
|
||||
return map->ops->map_local_storage_charge(smap, owner, size);
|
||||
}
|
||||
|
||||
static void mem_uncharge(struct bpf_local_storage_map *smap, void *owner,
|
||||
u32 size)
|
||||
{
|
||||
struct bpf_map *map = &smap->map;
|
||||
|
||||
if (map->ops->map_local_storage_uncharge)
|
||||
map->ops->map_local_storage_uncharge(smap, owner, size);
|
||||
}
|
||||
|
||||
static struct bpf_local_storage __rcu **
|
||||
owner_storage(struct bpf_local_storage_map *smap, void *owner)
|
||||
{
|
||||
struct bpf_map *map = &smap->map;
|
||||
|
||||
return map->ops->map_owner_storage_ptr(owner);
|
||||
}
|
||||
|
||||
static bool selem_linked_to_storage(const struct bpf_local_storage_elem *selem)
|
||||
{
|
||||
return !hlist_unhashed(&selem->snode);
|
||||
}
|
||||
|
||||
static bool selem_linked_to_map(const struct bpf_local_storage_elem *selem)
|
||||
{
|
||||
return !hlist_unhashed(&selem->map_node);
|
||||
}
|
||||
|
||||
struct bpf_local_storage_elem *
|
||||
bpf_selem_alloc(struct bpf_local_storage_map *smap, void *owner,
|
||||
void *value, bool charge_mem)
|
||||
{
|
||||
struct bpf_local_storage_elem *selem;
|
||||
|
||||
if (charge_mem && mem_charge(smap, owner, smap->elem_size))
|
||||
return NULL;
|
||||
|
||||
selem = kzalloc(smap->elem_size, GFP_ATOMIC | __GFP_NOWARN);
|
||||
if (selem) {
|
||||
if (value)
|
||||
memcpy(SDATA(selem)->data, value, smap->map.value_size);
|
||||
return selem;
|
||||
}
|
||||
|
||||
if (charge_mem)
|
||||
mem_uncharge(smap, owner, smap->elem_size);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* local_storage->lock must be held and selem->local_storage == local_storage.
|
||||
* The caller must ensure selem->smap is still valid to be
|
||||
* dereferenced for its smap->elem_size and smap->cache_idx.
|
||||
*/
|
||||
bool bpf_selem_unlink_storage_nolock(struct bpf_local_storage *local_storage,
|
||||
struct bpf_local_storage_elem *selem,
|
||||
bool uncharge_mem)
|
||||
{
|
||||
struct bpf_local_storage_map *smap;
|
||||
bool free_local_storage;
|
||||
void *owner;
|
||||
|
||||
smap = rcu_dereference(SDATA(selem)->smap);
|
||||
owner = local_storage->owner;
|
||||
|
||||
/* All uncharging on the owner must be done first.
|
||||
* The owner may be freed once the last selem is unlinked
|
||||
* from local_storage.
|
||||
*/
|
||||
if (uncharge_mem)
|
||||
mem_uncharge(smap, owner, smap->elem_size);
|
||||
|
||||
free_local_storage = hlist_is_singular_node(&selem->snode,
|
||||
&local_storage->list);
|
||||
if (free_local_storage) {
|
||||
mem_uncharge(smap, owner, sizeof(struct bpf_local_storage));
|
||||
local_storage->owner = NULL;
|
||||
|
||||
/* After this RCU_INIT, owner may be freed and cannot be used */
|
||||
RCU_INIT_POINTER(*owner_storage(smap, owner), NULL);
|
||||
|
||||
/* local_storage is not freed now. local_storage->lock is
|
||||
* still held and raw_spin_unlock_bh(&local_storage->lock)
|
||||
* will be done by the caller.
|
||||
*
|
||||
* Although the unlock will be done under
|
||||
* rcu_read_lock(), it is more intutivie to
|
||||
* read if kfree_rcu(local_storage, rcu) is done
|
||||
* after the raw_spin_unlock_bh(&local_storage->lock).
|
||||
*
|
||||
* Hence, a "bool free_local_storage" is returned
|
||||
* to the caller which then calls the kfree_rcu()
|
||||
* after unlock.
|
||||
*/
|
||||
}
|
||||
hlist_del_init_rcu(&selem->snode);
|
||||
if (rcu_access_pointer(local_storage->cache[smap->cache_idx]) ==
|
||||
SDATA(selem))
|
||||
RCU_INIT_POINTER(local_storage->cache[smap->cache_idx], NULL);
|
||||
|
||||
kfree_rcu(selem, rcu);
|
||||
|
||||
return free_local_storage;
|
||||
}
|
||||
|
||||
static void __bpf_selem_unlink_storage(struct bpf_local_storage_elem *selem)
|
||||
{
|
||||
struct bpf_local_storage *local_storage;
|
||||
bool free_local_storage = false;
|
||||
|
||||
if (unlikely(!selem_linked_to_storage(selem)))
|
||||
/* selem has already been unlinked from sk */
|
||||
return;
|
||||
|
||||
local_storage = rcu_dereference(selem->local_storage);
|
||||
raw_spin_lock_bh(&local_storage->lock);
|
||||
if (likely(selem_linked_to_storage(selem)))
|
||||
free_local_storage = bpf_selem_unlink_storage_nolock(
|
||||
local_storage, selem, true);
|
||||
raw_spin_unlock_bh(&local_storage->lock);
|
||||
|
||||
if (free_local_storage)
|
||||
kfree_rcu(local_storage, rcu);
|
||||
}
|
||||
|
||||
void bpf_selem_link_storage_nolock(struct bpf_local_storage *local_storage,
|
||||
struct bpf_local_storage_elem *selem)
|
||||
{
|
||||
RCU_INIT_POINTER(selem->local_storage, local_storage);
|
||||
hlist_add_head_rcu(&selem->snode, &local_storage->list);
|
||||
}
|
||||
|
||||
void bpf_selem_unlink_map(struct bpf_local_storage_elem *selem)
|
||||
{
|
||||
struct bpf_local_storage_map *smap;
|
||||
struct bpf_local_storage_map_bucket *b;
|
||||
|
||||
if (unlikely(!selem_linked_to_map(selem)))
|
||||
/* selem has already be unlinked from smap */
|
||||
return;
|
||||
|
||||
smap = rcu_dereference(SDATA(selem)->smap);
|
||||
b = select_bucket(smap, selem);
|
||||
raw_spin_lock_bh(&b->lock);
|
||||
if (likely(selem_linked_to_map(selem)))
|
||||
hlist_del_init_rcu(&selem->map_node);
|
||||
raw_spin_unlock_bh(&b->lock);
|
||||
}
|
||||
|
||||
void bpf_selem_link_map(struct bpf_local_storage_map *smap,
|
||||
struct bpf_local_storage_elem *selem)
|
||||
{
|
||||
struct bpf_local_storage_map_bucket *b = select_bucket(smap, selem);
|
||||
|
||||
raw_spin_lock_bh(&b->lock);
|
||||
RCU_INIT_POINTER(SDATA(selem)->smap, smap);
|
||||
hlist_add_head_rcu(&selem->map_node, &b->list);
|
||||
raw_spin_unlock_bh(&b->lock);
|
||||
}
|
||||
|
||||
void bpf_selem_unlink(struct bpf_local_storage_elem *selem)
|
||||
{
|
||||
/* Always unlink from map before unlinking from local_storage
|
||||
* because selem will be freed after successfully unlinked from
|
||||
* the local_storage.
|
||||
*/
|
||||
bpf_selem_unlink_map(selem);
|
||||
__bpf_selem_unlink_storage(selem);
|
||||
}
|
||||
|
||||
struct bpf_local_storage_data *
|
||||
bpf_local_storage_lookup(struct bpf_local_storage *local_storage,
|
||||
struct bpf_local_storage_map *smap,
|
||||
bool cacheit_lockit)
|
||||
{
|
||||
struct bpf_local_storage_data *sdata;
|
||||
struct bpf_local_storage_elem *selem;
|
||||
|
||||
/* Fast path (cache hit) */
|
||||
sdata = rcu_dereference(local_storage->cache[smap->cache_idx]);
|
||||
if (sdata && rcu_access_pointer(sdata->smap) == smap)
|
||||
return sdata;
|
||||
|
||||
/* Slow path (cache miss) */
|
||||
hlist_for_each_entry_rcu(selem, &local_storage->list, snode)
|
||||
if (rcu_access_pointer(SDATA(selem)->smap) == smap)
|
||||
break;
|
||||
|
||||
if (!selem)
|
||||
return NULL;
|
||||
|
||||
sdata = SDATA(selem);
|
||||
if (cacheit_lockit) {
|
||||
/* spinlock is needed to avoid racing with the
|
||||
* parallel delete. Otherwise, publishing an already
|
||||
* deleted sdata to the cache will become a use-after-free
|
||||
* problem in the next bpf_local_storage_lookup().
|
||||
*/
|
||||
raw_spin_lock_bh(&local_storage->lock);
|
||||
if (selem_linked_to_storage(selem))
|
||||
rcu_assign_pointer(local_storage->cache[smap->cache_idx],
|
||||
sdata);
|
||||
raw_spin_unlock_bh(&local_storage->lock);
|
||||
}
|
||||
|
||||
return sdata;
|
||||
}
|
||||
|
||||
static int check_flags(const struct bpf_local_storage_data *old_sdata,
|
||||
u64 map_flags)
|
||||
{
|
||||
if (old_sdata && (map_flags & ~BPF_F_LOCK) == BPF_NOEXIST)
|
||||
/* elem already exists */
|
||||
return -EEXIST;
|
||||
|
||||
if (!old_sdata && (map_flags & ~BPF_F_LOCK) == BPF_EXIST)
|
||||
/* elem doesn't exist, cannot update it */
|
||||
return -ENOENT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bpf_local_storage_alloc(void *owner,
|
||||
struct bpf_local_storage_map *smap,
|
||||
struct bpf_local_storage_elem *first_selem)
|
||||
{
|
||||
struct bpf_local_storage *prev_storage, *storage;
|
||||
struct bpf_local_storage **owner_storage_ptr;
|
||||
int err;
|
||||
|
||||
err = mem_charge(smap, owner, sizeof(*storage));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
storage = kzalloc(sizeof(*storage), GFP_ATOMIC | __GFP_NOWARN);
|
||||
if (!storage) {
|
||||
err = -ENOMEM;
|
||||
goto uncharge;
|
||||
}
|
||||
|
||||
INIT_HLIST_HEAD(&storage->list);
|
||||
raw_spin_lock_init(&storage->lock);
|
||||
storage->owner = owner;
|
||||
|
||||
bpf_selem_link_storage_nolock(storage, first_selem);
|
||||
bpf_selem_link_map(smap, first_selem);
|
||||
|
||||
owner_storage_ptr =
|
||||
(struct bpf_local_storage **)owner_storage(smap, owner);
|
||||
/* Publish storage to the owner.
|
||||
* Instead of using any lock of the kernel object (i.e. owner),
|
||||
* cmpxchg will work with any kernel object regardless what
|
||||
* the running context is, bh, irq...etc.
|
||||
*
|
||||
* From now on, the owner->storage pointer (e.g. sk->sk_bpf_storage)
|
||||
* is protected by the storage->lock. Hence, when freeing
|
||||
* the owner->storage, the storage->lock must be held before
|
||||
* setting owner->storage ptr to NULL.
|
||||
*/
|
||||
prev_storage = cmpxchg(owner_storage_ptr, NULL, storage);
|
||||
if (unlikely(prev_storage)) {
|
||||
bpf_selem_unlink_map(first_selem);
|
||||
err = -EAGAIN;
|
||||
goto uncharge;
|
||||
|
||||
/* Note that even first_selem was linked to smap's
|
||||
* bucket->list, first_selem can be freed immediately
|
||||
* (instead of kfree_rcu) because
|
||||
* bpf_local_storage_map_free() does a
|
||||
* synchronize_rcu() before walking the bucket->list.
|
||||
* Hence, no one is accessing selem from the
|
||||
* bucket->list under rcu_read_lock().
|
||||
*/
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
uncharge:
|
||||
kfree(storage);
|
||||
mem_uncharge(smap, owner, sizeof(*storage));
|
||||
return err;
|
||||
}
|
||||
|
||||
/* sk cannot be going away because it is linking new elem
|
||||
* to sk->sk_bpf_storage. (i.e. sk->sk_refcnt cannot be 0).
|
||||
* Otherwise, it will become a leak (and other memory issues
|
||||
* during map destruction).
|
||||
*/
|
||||
struct bpf_local_storage_data *
|
||||
bpf_local_storage_update(void *owner, struct bpf_local_storage_map *smap,
|
||||
void *value, u64 map_flags)
|
||||
{
|
||||
struct bpf_local_storage_data *old_sdata = NULL;
|
||||
struct bpf_local_storage_elem *selem;
|
||||
struct bpf_local_storage *local_storage;
|
||||
int err;
|
||||
|
||||
/* BPF_EXIST and BPF_NOEXIST cannot be both set */
|
||||
if (unlikely((map_flags & ~BPF_F_LOCK) > BPF_EXIST) ||
|
||||
/* BPF_F_LOCK can only be used in a value with spin_lock */
|
||||
unlikely((map_flags & BPF_F_LOCK) &&
|
||||
!map_value_has_spin_lock(&smap->map)))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
local_storage = rcu_dereference(*owner_storage(smap, owner));
|
||||
if (!local_storage || hlist_empty(&local_storage->list)) {
|
||||
/* Very first elem for the owner */
|
||||
err = check_flags(NULL, map_flags);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
|
||||
selem = bpf_selem_alloc(smap, owner, value, true);
|
||||
if (!selem)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
err = bpf_local_storage_alloc(owner, smap, selem);
|
||||
if (err) {
|
||||
kfree(selem);
|
||||
mem_uncharge(smap, owner, smap->elem_size);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
return SDATA(selem);
|
||||
}
|
||||
|
||||
if ((map_flags & BPF_F_LOCK) && !(map_flags & BPF_NOEXIST)) {
|
||||
/* Hoping to find an old_sdata to do inline update
|
||||
* such that it can avoid taking the local_storage->lock
|
||||
* and changing the lists.
|
||||
*/
|
||||
old_sdata =
|
||||
bpf_local_storage_lookup(local_storage, smap, false);
|
||||
err = check_flags(old_sdata, map_flags);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
if (old_sdata && selem_linked_to_storage(SELEM(old_sdata))) {
|
||||
copy_map_value_locked(&smap->map, old_sdata->data,
|
||||
value, false);
|
||||
return old_sdata;
|
||||
}
|
||||
}
|
||||
|
||||
raw_spin_lock_bh(&local_storage->lock);
|
||||
|
||||
/* Recheck local_storage->list under local_storage->lock */
|
||||
if (unlikely(hlist_empty(&local_storage->list))) {
|
||||
/* A parallel del is happening and local_storage is going
|
||||
* away. It has just been checked before, so very
|
||||
* unlikely. Return instead of retry to keep things
|
||||
* simple.
|
||||
*/
|
||||
err = -EAGAIN;
|
||||
goto unlock_err;
|
||||
}
|
||||
|
||||
old_sdata = bpf_local_storage_lookup(local_storage, smap, false);
|
||||
err = check_flags(old_sdata, map_flags);
|
||||
if (err)
|
||||
goto unlock_err;
|
||||
|
||||
if (old_sdata && (map_flags & BPF_F_LOCK)) {
|
||||
copy_map_value_locked(&smap->map, old_sdata->data, value,
|
||||
false);
|
||||
selem = SELEM(old_sdata);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/* local_storage->lock is held. Hence, we are sure
|
||||
* we can unlink and uncharge the old_sdata successfully
|
||||
* later. Hence, instead of charging the new selem now
|
||||
* and then uncharge the old selem later (which may cause
|
||||
* a potential but unnecessary charge failure), avoid taking
|
||||
* a charge at all here (the "!old_sdata" check) and the
|
||||
* old_sdata will not be uncharged later during
|
||||
* bpf_selem_unlink_storage_nolock().
|
||||
*/
|
||||
selem = bpf_selem_alloc(smap, owner, value, !old_sdata);
|
||||
if (!selem) {
|
||||
err = -ENOMEM;
|
||||
goto unlock_err;
|
||||
}
|
||||
|
||||
/* First, link the new selem to the map */
|
||||
bpf_selem_link_map(smap, selem);
|
||||
|
||||
/* Second, link (and publish) the new selem to local_storage */
|
||||
bpf_selem_link_storage_nolock(local_storage, selem);
|
||||
|
||||
/* Third, remove old selem, SELEM(old_sdata) */
|
||||
if (old_sdata) {
|
||||
bpf_selem_unlink_map(SELEM(old_sdata));
|
||||
bpf_selem_unlink_storage_nolock(local_storage, SELEM(old_sdata),
|
||||
false);
|
||||
}
|
||||
|
||||
unlock:
|
||||
raw_spin_unlock_bh(&local_storage->lock);
|
||||
return SDATA(selem);
|
||||
|
||||
unlock_err:
|
||||
raw_spin_unlock_bh(&local_storage->lock);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
u16 bpf_local_storage_cache_idx_get(struct bpf_local_storage_cache *cache)
|
||||
{
|
||||
u64 min_usage = U64_MAX;
|
||||
u16 i, res = 0;
|
||||
|
||||
spin_lock(&cache->idx_lock);
|
||||
|
||||
for (i = 0; i < BPF_LOCAL_STORAGE_CACHE_SIZE; i++) {
|
||||
if (cache->idx_usage_counts[i] < min_usage) {
|
||||
min_usage = cache->idx_usage_counts[i];
|
||||
res = i;
|
||||
|
||||
/* Found a free cache_idx */
|
||||
if (!min_usage)
|
||||
break;
|
||||
}
|
||||
}
|
||||
cache->idx_usage_counts[res]++;
|
||||
|
||||
spin_unlock(&cache->idx_lock);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void bpf_local_storage_cache_idx_free(struct bpf_local_storage_cache *cache,
|
||||
u16 idx)
|
||||
{
|
||||
spin_lock(&cache->idx_lock);
|
||||
cache->idx_usage_counts[idx]--;
|
||||
spin_unlock(&cache->idx_lock);
|
||||
}
|
||||
|
||||
void bpf_local_storage_map_free(struct bpf_local_storage_map *smap)
|
||||
{
|
||||
struct bpf_local_storage_elem *selem;
|
||||
struct bpf_local_storage_map_bucket *b;
|
||||
unsigned int i;
|
||||
|
||||
/* Note that this map might be concurrently cloned from
|
||||
* bpf_sk_storage_clone. Wait for any existing bpf_sk_storage_clone
|
||||
* RCU read section to finish before proceeding. New RCU
|
||||
* read sections should be prevented via bpf_map_inc_not_zero.
|
||||
*/
|
||||
synchronize_rcu();
|
||||
|
||||
/* bpf prog and the userspace can no longer access this map
|
||||
* now. No new selem (of this map) can be added
|
||||
* to the owner->storage or to the map bucket's list.
|
||||
*
|
||||
* The elem of this map can be cleaned up here
|
||||
* or when the storage is freed e.g.
|
||||
* by bpf_sk_storage_free() during __sk_destruct().
|
||||
*/
|
||||
for (i = 0; i < (1U << smap->bucket_log); i++) {
|
||||
b = &smap->buckets[i];
|
||||
|
||||
rcu_read_lock();
|
||||
/* No one is adding to b->list now */
|
||||
while ((selem = hlist_entry_safe(
|
||||
rcu_dereference_raw(hlist_first_rcu(&b->list)),
|
||||
struct bpf_local_storage_elem, map_node))) {
|
||||
bpf_selem_unlink(selem);
|
||||
cond_resched_rcu();
|
||||
}
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
/* While freeing the storage we may still need to access the map.
|
||||
*
|
||||
* e.g. when bpf_sk_storage_free() has unlinked selem from the map
|
||||
* which then made the above while((selem = ...)) loop
|
||||
* exit immediately.
|
||||
*
|
||||
* However, while freeing the storage one still needs to access the
|
||||
* smap->elem_size to do the uncharging in
|
||||
* bpf_selem_unlink_storage_nolock().
|
||||
*
|
||||
* Hence, wait another rcu grace period for the storage to be freed.
|
||||
*/
|
||||
synchronize_rcu();
|
||||
|
||||
kvfree(smap->buckets);
|
||||
kfree(smap);
|
||||
}
|
||||
|
||||
int bpf_local_storage_map_alloc_check(union bpf_attr *attr)
|
||||
{
|
||||
if (attr->map_flags & ~BPF_LOCAL_STORAGE_CREATE_FLAG_MASK ||
|
||||
!(attr->map_flags & BPF_F_NO_PREALLOC) ||
|
||||
attr->max_entries ||
|
||||
attr->key_size != sizeof(int) || !attr->value_size ||
|
||||
/* Enforce BTF for userspace sk dumping */
|
||||
!attr->btf_key_type_id || !attr->btf_value_type_id)
|
||||
return -EINVAL;
|
||||
|
||||
if (!bpf_capable())
|
||||
return -EPERM;
|
||||
|
||||
if (attr->value_size > BPF_LOCAL_STORAGE_MAX_VALUE_SIZE)
|
||||
return -E2BIG;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct bpf_local_storage_map *bpf_local_storage_map_alloc(union bpf_attr *attr)
|
||||
{
|
||||
struct bpf_local_storage_map *smap;
|
||||
unsigned int i;
|
||||
u32 nbuckets;
|
||||
u64 cost;
|
||||
int ret;
|
||||
|
||||
smap = kzalloc(sizeof(*smap), GFP_USER | __GFP_NOWARN);
|
||||
if (!smap)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
bpf_map_init_from_attr(&smap->map, attr);
|
||||
|
||||
nbuckets = roundup_pow_of_two(num_possible_cpus());
|
||||
/* Use at least 2 buckets, select_bucket() is undefined behavior with 1 bucket */
|
||||
nbuckets = max_t(u32, 2, nbuckets);
|
||||
smap->bucket_log = ilog2(nbuckets);
|
||||
cost = sizeof(*smap->buckets) * nbuckets + sizeof(*smap);
|
||||
|
||||
ret = bpf_map_charge_init(&smap->map.memory, cost);
|
||||
if (ret < 0) {
|
||||
kfree(smap);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
smap->buckets = kvcalloc(sizeof(*smap->buckets), nbuckets,
|
||||
GFP_USER | __GFP_NOWARN);
|
||||
if (!smap->buckets) {
|
||||
bpf_map_charge_finish(&smap->map.memory);
|
||||
kfree(smap);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
for (i = 0; i < nbuckets; i++) {
|
||||
INIT_HLIST_HEAD(&smap->buckets[i].list);
|
||||
raw_spin_lock_init(&smap->buckets[i].lock);
|
||||
}
|
||||
|
||||
smap->elem_size =
|
||||
sizeof(struct bpf_local_storage_elem) + attr->value_size;
|
||||
|
||||
return smap;
|
||||
}
|
||||
|
||||
int bpf_local_storage_map_check_btf(const struct bpf_map *map,
|
||||
const struct btf *btf,
|
||||
const struct btf_type *key_type,
|
||||
const struct btf_type *value_type)
|
||||
{
|
||||
u32 int_data;
|
||||
|
||||
if (BTF_INFO_KIND(key_type->info) != BTF_KIND_INT)
|
||||
return -EINVAL;
|
||||
|
||||
int_data = *(u32 *)(key_type + 1);
|
||||
if (BTF_INT_BITS(int_data) != 32 || BTF_INT_OFFSET(int_data))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
+20
-1
@@ -11,6 +11,8 @@
|
||||
#include <linux/bpf_lsm.h>
|
||||
#include <linux/kallsyms.h>
|
||||
#include <linux/bpf_verifier.h>
|
||||
#include <net/bpf_sk_storage.h>
|
||||
#include <linux/bpf_local_storage.h>
|
||||
|
||||
/* For every LSM hook that allows attachment of BPF programs, declare a nop
|
||||
* function where a BPF program can be attached.
|
||||
@@ -45,10 +47,27 @@ int bpf_lsm_verify_prog(struct bpf_verifier_log *vlog,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct bpf_func_proto *
|
||||
bpf_lsm_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
|
||||
{
|
||||
switch (func_id) {
|
||||
case BPF_FUNC_inode_storage_get:
|
||||
return &bpf_inode_storage_get_proto;
|
||||
case BPF_FUNC_inode_storage_delete:
|
||||
return &bpf_inode_storage_delete_proto;
|
||||
case BPF_FUNC_sk_storage_get:
|
||||
return &bpf_sk_storage_get_proto;
|
||||
case BPF_FUNC_sk_storage_delete:
|
||||
return &bpf_sk_storage_delete_proto;
|
||||
default:
|
||||
return tracing_prog_func_proto(func_id, prog);
|
||||
}
|
||||
}
|
||||
|
||||
const struct bpf_prog_ops lsm_prog_ops = {
|
||||
};
|
||||
|
||||
const struct bpf_verifier_ops lsm_verifier_ops = {
|
||||
.get_func_proto = tracing_prog_func_proto,
|
||||
.get_func_proto = bpf_lsm_func_proto,
|
||||
.is_valid_access = btf_ctx_access,
|
||||
};
|
||||
|
||||
@@ -298,8 +298,7 @@ static int check_zero_holes(const struct btf_type *t, void *data)
|
||||
return -EINVAL;
|
||||
|
||||
mtype = btf_type_by_id(btf_vmlinux, member->type);
|
||||
mtype = btf_resolve_size(btf_vmlinux, mtype, &msize,
|
||||
NULL, NULL);
|
||||
mtype = btf_resolve_size(btf_vmlinux, mtype, &msize);
|
||||
if (IS_ERR(mtype))
|
||||
return PTR_ERR(mtype);
|
||||
prev_mend = moff + msize;
|
||||
@@ -396,8 +395,7 @@ static int bpf_struct_ops_map_update_elem(struct bpf_map *map, void *key,
|
||||
u32 msize;
|
||||
|
||||
mtype = btf_type_by_id(btf_vmlinux, member->type);
|
||||
mtype = btf_resolve_size(btf_vmlinux, mtype, &msize,
|
||||
NULL, NULL);
|
||||
mtype = btf_resolve_size(btf_vmlinux, mtype, &msize);
|
||||
if (IS_ERR(mtype)) {
|
||||
err = PTR_ERR(mtype);
|
||||
goto reset_unlock;
|
||||
|
||||
+1058
-171
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -1226,7 +1226,7 @@ const struct bpf_verifier_ops cg_dev_verifier_ops = {
|
||||
*/
|
||||
int __cgroup_bpf_run_filter_sysctl(struct ctl_table_header *head,
|
||||
struct ctl_table *table, int write,
|
||||
void **buf, size_t *pcount, loff_t *ppos,
|
||||
char **buf, size_t *pcount, loff_t *ppos,
|
||||
enum bpf_attach_type type)
|
||||
{
|
||||
struct bpf_sysctl_kern ctx = {
|
||||
@@ -1794,7 +1794,7 @@ static bool cg_sockopt_is_valid_access(int off, int size,
|
||||
return prog->expected_attach_type ==
|
||||
BPF_CGROUP_GETSOCKOPT;
|
||||
case offsetof(struct bpf_sockopt, optname):
|
||||
/* fallthrough */
|
||||
fallthrough;
|
||||
case offsetof(struct bpf_sockopt, level):
|
||||
if (size != size_default)
|
||||
return false;
|
||||
|
||||
+22
-9
@@ -25,7 +25,7 @@
|
||||
#include <linux/moduleloader.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/btf.h>
|
||||
#include <linux/frame.h>
|
||||
#include <linux/objtool.h>
|
||||
#include <linux/rbtree_latch.h>
|
||||
#include <linux/kallsyms.h>
|
||||
#include <linux/rcupdate.h>
|
||||
@@ -98,6 +98,8 @@ struct bpf_prog *bpf_prog_alloc_no_stats(unsigned int size, gfp_t gfp_extra_flag
|
||||
fp->jit_requested = ebpf_jit_enabled();
|
||||
|
||||
INIT_LIST_HEAD_RCU(&fp->aux->ksym.lnode);
|
||||
mutex_init(&fp->aux->used_maps_mutex);
|
||||
mutex_init(&fp->aux->dst_mutex);
|
||||
|
||||
return fp;
|
||||
}
|
||||
@@ -253,6 +255,8 @@ struct bpf_prog *bpf_prog_realloc(struct bpf_prog *fp_old, unsigned int size,
|
||||
void __bpf_prog_free(struct bpf_prog *fp)
|
||||
{
|
||||
if (fp->aux) {
|
||||
mutex_destroy(&fp->aux->used_maps_mutex);
|
||||
mutex_destroy(&fp->aux->dst_mutex);
|
||||
free_percpu(fp->aux->stats);
|
||||
kfree(fp->aux->poke_tab);
|
||||
kfree(fp->aux);
|
||||
@@ -773,7 +777,8 @@ int bpf_jit_add_poke_descriptor(struct bpf_prog *prog,
|
||||
|
||||
if (size > poke_tab_max)
|
||||
return -ENOSPC;
|
||||
if (poke->ip || poke->ip_stable || poke->adj_off)
|
||||
if (poke->tailcall_target || poke->tailcall_target_stable ||
|
||||
poke->tailcall_bypass || poke->adj_off || poke->bypass_addr)
|
||||
return -EINVAL;
|
||||
|
||||
switch (poke->reason) {
|
||||
@@ -1747,8 +1752,9 @@ bool bpf_prog_array_compatible(struct bpf_array *array,
|
||||
static int bpf_check_tail_call(const struct bpf_prog *fp)
|
||||
{
|
||||
struct bpf_prog_aux *aux = fp->aux;
|
||||
int i;
|
||||
int i, ret = 0;
|
||||
|
||||
mutex_lock(&aux->used_maps_mutex);
|
||||
for (i = 0; i < aux->used_map_cnt; i++) {
|
||||
struct bpf_map *map = aux->used_maps[i];
|
||||
struct bpf_array *array;
|
||||
@@ -1757,11 +1763,15 @@ static int bpf_check_tail_call(const struct bpf_prog *fp)
|
||||
continue;
|
||||
|
||||
array = container_of(map, struct bpf_array, map);
|
||||
if (!bpf_prog_array_compatible(array, fp))
|
||||
return -EINVAL;
|
||||
if (!bpf_prog_array_compatible(array, fp)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
out:
|
||||
mutex_unlock(&aux->used_maps_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void bpf_prog_select_func(struct bpf_prog *fp)
|
||||
@@ -2130,7 +2140,8 @@ static void bpf_prog_free_deferred(struct work_struct *work)
|
||||
if (aux->prog->has_callchain_buf)
|
||||
put_callchain_buffers();
|
||||
#endif
|
||||
bpf_trampoline_put(aux->trampoline);
|
||||
if (aux->dst_trampoline)
|
||||
bpf_trampoline_put(aux->dst_trampoline);
|
||||
for (i = 0; i < aux->func_cnt; i++)
|
||||
bpf_jit_free(aux->func[i]);
|
||||
if (aux->func_cnt) {
|
||||
@@ -2146,8 +2157,8 @@ void bpf_prog_free(struct bpf_prog *fp)
|
||||
{
|
||||
struct bpf_prog_aux *aux = fp->aux;
|
||||
|
||||
if (aux->linked_prog)
|
||||
bpf_prog_put(aux->linked_prog);
|
||||
if (aux->dst_prog)
|
||||
bpf_prog_put(aux->dst_prog);
|
||||
INIT_WORK(&aux->work, bpf_prog_free_deferred);
|
||||
schedule_work(&aux->work);
|
||||
}
|
||||
@@ -2208,6 +2219,8 @@ const struct bpf_func_proto bpf_get_current_cgroup_id_proto __weak;
|
||||
const struct bpf_func_proto bpf_get_current_ancestor_cgroup_id_proto __weak;
|
||||
const struct bpf_func_proto bpf_get_local_storage_proto __weak;
|
||||
const struct bpf_func_proto bpf_get_ns_current_pid_tgid_proto __weak;
|
||||
const struct bpf_func_proto bpf_snprintf_btf_proto __weak;
|
||||
const struct bpf_func_proto bpf_seq_printf_btf_proto __weak;
|
||||
|
||||
const struct bpf_func_proto * __weak bpf_get_trace_printk_proto(void)
|
||||
{
|
||||
|
||||
+7
-12
@@ -79,8 +79,6 @@ struct bpf_cpu_map {
|
||||
|
||||
static DEFINE_PER_CPU(struct list_head, cpu_map_flush_list);
|
||||
|
||||
static int bq_flush_to_queue(struct xdp_bulk_queue *bq);
|
||||
|
||||
static struct bpf_map *cpu_map_alloc(union bpf_attr *attr)
|
||||
{
|
||||
u32 value_size = attr->value_size;
|
||||
@@ -157,8 +155,7 @@ static void cpu_map_kthread_stop(struct work_struct *work)
|
||||
kthread_stop(rcpu->kthread);
|
||||
}
|
||||
|
||||
static struct sk_buff *cpu_map_build_skb(struct bpf_cpu_map_entry *rcpu,
|
||||
struct xdp_frame *xdpf,
|
||||
static struct sk_buff *cpu_map_build_skb(struct xdp_frame *xdpf,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
unsigned int hard_start_headroom;
|
||||
@@ -279,7 +276,7 @@ static int cpu_map_bpf_prog_run_xdp(struct bpf_cpu_map_entry *rcpu,
|
||||
break;
|
||||
default:
|
||||
bpf_warn_invalid_xdp_action(act);
|
||||
/* fallthrough */
|
||||
fallthrough;
|
||||
case XDP_DROP:
|
||||
xdp_return_frame(xdpf);
|
||||
stats->drop++;
|
||||
@@ -367,7 +364,7 @@ static int cpu_map_kthread_run(void *data)
|
||||
struct sk_buff *skb = skbs[i];
|
||||
int ret;
|
||||
|
||||
skb = cpu_map_build_skb(rcpu, xdpf, skb);
|
||||
skb = cpu_map_build_skb(xdpf, skb);
|
||||
if (!skb) {
|
||||
xdp_return_frame(xdpf);
|
||||
continue;
|
||||
@@ -658,6 +655,7 @@ static int cpu_map_get_next_key(struct bpf_map *map, void *key, void *next_key)
|
||||
|
||||
static int cpu_map_btf_id;
|
||||
const struct bpf_map_ops cpu_map_ops = {
|
||||
.map_meta_equal = bpf_map_meta_equal,
|
||||
.map_alloc = cpu_map_alloc,
|
||||
.map_free = cpu_map_free,
|
||||
.map_delete_elem = cpu_map_delete_elem,
|
||||
@@ -669,7 +667,7 @@ const struct bpf_map_ops cpu_map_ops = {
|
||||
.map_btf_id = &cpu_map_btf_id,
|
||||
};
|
||||
|
||||
static int bq_flush_to_queue(struct xdp_bulk_queue *bq)
|
||||
static void bq_flush_to_queue(struct xdp_bulk_queue *bq)
|
||||
{
|
||||
struct bpf_cpu_map_entry *rcpu = bq->obj;
|
||||
unsigned int processed = 0, drops = 0;
|
||||
@@ -678,7 +676,7 @@ static int bq_flush_to_queue(struct xdp_bulk_queue *bq)
|
||||
int i;
|
||||
|
||||
if (unlikely(!bq->count))
|
||||
return 0;
|
||||
return;
|
||||
|
||||
q = rcpu->queue;
|
||||
spin_lock(&q->producer_lock);
|
||||
@@ -701,13 +699,12 @@ static int bq_flush_to_queue(struct xdp_bulk_queue *bq)
|
||||
|
||||
/* Feedback loop via tracepoints */
|
||||
trace_xdp_cpumap_enqueue(rcpu->map_id, processed, drops, to_cpu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Runs under RCU-read-side, plus in softirq under NAPI protection.
|
||||
* Thus, safe percpu variable access.
|
||||
*/
|
||||
static int bq_enqueue(struct bpf_cpu_map_entry *rcpu, struct xdp_frame *xdpf)
|
||||
static void bq_enqueue(struct bpf_cpu_map_entry *rcpu, struct xdp_frame *xdpf)
|
||||
{
|
||||
struct list_head *flush_list = this_cpu_ptr(&cpu_map_flush_list);
|
||||
struct xdp_bulk_queue *bq = this_cpu_ptr(rcpu->bulkq);
|
||||
@@ -728,8 +725,6 @@ static int bq_enqueue(struct bpf_cpu_map_entry *rcpu, struct xdp_frame *xdpf)
|
||||
|
||||
if (!bq->flush_node.prev)
|
||||
list_add(&bq->flush_node, flush_list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cpu_map_enqueue(struct bpf_cpu_map_entry *rcpu, struct xdp_buff *xdp,
|
||||
|
||||
+9
-8
@@ -341,14 +341,14 @@ bool dev_map_can_have_prog(struct bpf_map *map)
|
||||
return false;
|
||||
}
|
||||
|
||||
static int bq_xmit_all(struct xdp_dev_bulk_queue *bq, u32 flags)
|
||||
static void bq_xmit_all(struct xdp_dev_bulk_queue *bq, u32 flags)
|
||||
{
|
||||
struct net_device *dev = bq->dev;
|
||||
int sent = 0, drops = 0, err = 0;
|
||||
int i;
|
||||
|
||||
if (unlikely(!bq->count))
|
||||
return 0;
|
||||
return;
|
||||
|
||||
for (i = 0; i < bq->count; i++) {
|
||||
struct xdp_frame *xdpf = bq->q[i];
|
||||
@@ -369,7 +369,7 @@ out:
|
||||
trace_xdp_devmap_xmit(bq->dev_rx, dev, sent, drops, err);
|
||||
bq->dev_rx = NULL;
|
||||
__list_del_clearprev(&bq->flush_node);
|
||||
return 0;
|
||||
return;
|
||||
error:
|
||||
/* If ndo_xdp_xmit fails with an errno, no frames have been
|
||||
* xmit'ed and it's our responsibility to them free all.
|
||||
@@ -421,8 +421,8 @@ struct bpf_dtab_netdev *__dev_map_lookup_elem(struct bpf_map *map, u32 key)
|
||||
/* Runs under RCU-read-side, plus in softirq under NAPI protection.
|
||||
* Thus, safe percpu variable access.
|
||||
*/
|
||||
static int bq_enqueue(struct net_device *dev, struct xdp_frame *xdpf,
|
||||
struct net_device *dev_rx)
|
||||
static void bq_enqueue(struct net_device *dev, struct xdp_frame *xdpf,
|
||||
struct net_device *dev_rx)
|
||||
{
|
||||
struct list_head *flush_list = this_cpu_ptr(&dev_flush_list);
|
||||
struct xdp_dev_bulk_queue *bq = this_cpu_ptr(dev->xdp_bulkq);
|
||||
@@ -441,8 +441,6 @@ static int bq_enqueue(struct net_device *dev, struct xdp_frame *xdpf,
|
||||
|
||||
if (!bq->flush_node.prev)
|
||||
list_add(&bq->flush_node, flush_list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int __xdp_enqueue(struct net_device *dev, struct xdp_buff *xdp,
|
||||
@@ -462,7 +460,8 @@ static inline int __xdp_enqueue(struct net_device *dev, struct xdp_buff *xdp,
|
||||
if (unlikely(!xdpf))
|
||||
return -EOVERFLOW;
|
||||
|
||||
return bq_enqueue(dev, xdpf, dev_rx);
|
||||
bq_enqueue(dev, xdpf, dev_rx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct xdp_buff *dev_map_run_prog(struct net_device *dev,
|
||||
@@ -751,6 +750,7 @@ static int dev_map_hash_update_elem(struct bpf_map *map, void *key, void *value,
|
||||
|
||||
static int dev_map_btf_id;
|
||||
const struct bpf_map_ops dev_map_ops = {
|
||||
.map_meta_equal = bpf_map_meta_equal,
|
||||
.map_alloc = dev_map_alloc,
|
||||
.map_free = dev_map_free,
|
||||
.map_get_next_key = dev_map_get_next_key,
|
||||
@@ -764,6 +764,7 @@ const struct bpf_map_ops dev_map_ops = {
|
||||
|
||||
static int dev_map_hash_map_btf_id;
|
||||
const struct bpf_map_ops dev_map_hash_ops = {
|
||||
.map_meta_equal = bpf_map_meta_equal,
|
||||
.map_alloc = dev_map_alloc,
|
||||
.map_free = dev_map_free,
|
||||
.map_get_next_key = dev_map_hash_get_next_key,
|
||||
|
||||
+17
-20
@@ -9,6 +9,7 @@
|
||||
#include <linux/rculist_nulls.h>
|
||||
#include <linux/random.h>
|
||||
#include <uapi/linux/btf.h>
|
||||
#include <linux/rcupdate_trace.h>
|
||||
#include "percpu_freelist.h"
|
||||
#include "bpf_lru_list.h"
|
||||
#include "map_in_map.h"
|
||||
@@ -577,8 +578,7 @@ static void *__htab_map_lookup_elem(struct bpf_map *map, void *key)
|
||||
struct htab_elem *l;
|
||||
u32 hash, key_size;
|
||||
|
||||
/* Must be called with rcu_read_lock. */
|
||||
WARN_ON_ONCE(!rcu_read_lock_held());
|
||||
WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held());
|
||||
|
||||
key_size = map->key_size;
|
||||
|
||||
@@ -612,7 +612,7 @@ static void *htab_map_lookup_elem(struct bpf_map *map, void *key)
|
||||
* bpf_prog
|
||||
* __htab_map_lookup_elem
|
||||
*/
|
||||
static u32 htab_map_gen_lookup(struct bpf_map *map, struct bpf_insn *insn_buf)
|
||||
static int htab_map_gen_lookup(struct bpf_map *map, struct bpf_insn *insn_buf)
|
||||
{
|
||||
struct bpf_insn *insn = insn_buf;
|
||||
const int ret = BPF_REG_0;
|
||||
@@ -651,7 +651,7 @@ static void *htab_lru_map_lookup_elem_sys(struct bpf_map *map, void *key)
|
||||
return __htab_lru_map_lookup_elem(map, key, false);
|
||||
}
|
||||
|
||||
static u32 htab_lru_map_gen_lookup(struct bpf_map *map,
|
||||
static int htab_lru_map_gen_lookup(struct bpf_map *map,
|
||||
struct bpf_insn *insn_buf)
|
||||
{
|
||||
struct bpf_insn *insn = insn_buf;
|
||||
@@ -941,7 +941,7 @@ static int htab_map_update_elem(struct bpf_map *map, void *key, void *value,
|
||||
/* unknown flags */
|
||||
return -EINVAL;
|
||||
|
||||
WARN_ON_ONCE(!rcu_read_lock_held());
|
||||
WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held());
|
||||
|
||||
key_size = map->key_size;
|
||||
|
||||
@@ -1032,7 +1032,7 @@ static int htab_lru_map_update_elem(struct bpf_map *map, void *key, void *value,
|
||||
/* unknown flags */
|
||||
return -EINVAL;
|
||||
|
||||
WARN_ON_ONCE(!rcu_read_lock_held());
|
||||
WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held());
|
||||
|
||||
key_size = map->key_size;
|
||||
|
||||
@@ -1220,7 +1220,7 @@ static int htab_map_delete_elem(struct bpf_map *map, void *key)
|
||||
u32 hash, key_size;
|
||||
int ret = -ENOENT;
|
||||
|
||||
WARN_ON_ONCE(!rcu_read_lock_held());
|
||||
WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held());
|
||||
|
||||
key_size = map->key_size;
|
||||
|
||||
@@ -1252,7 +1252,7 @@ static int htab_lru_map_delete_elem(struct bpf_map *map, void *key)
|
||||
u32 hash, key_size;
|
||||
int ret = -ENOENT;
|
||||
|
||||
WARN_ON_ONCE(!rcu_read_lock_held());
|
||||
WARN_ON_ONCE(!rcu_read_lock_held() && !rcu_read_lock_trace_held());
|
||||
|
||||
key_size = map->key_size;
|
||||
|
||||
@@ -1622,7 +1622,6 @@ struct bpf_iter_seq_hash_map_info {
|
||||
struct bpf_map *map;
|
||||
struct bpf_htab *htab;
|
||||
void *percpu_value_buf; // non-zero means percpu hash
|
||||
unsigned long flags;
|
||||
u32 bucket_id;
|
||||
u32 skip_elems;
|
||||
};
|
||||
@@ -1632,7 +1631,6 @@ bpf_hash_map_seq_find_next(struct bpf_iter_seq_hash_map_info *info,
|
||||
struct htab_elem *prev_elem)
|
||||
{
|
||||
const struct bpf_htab *htab = info->htab;
|
||||
unsigned long flags = info->flags;
|
||||
u32 skip_elems = info->skip_elems;
|
||||
u32 bucket_id = info->bucket_id;
|
||||
struct hlist_nulls_head *head;
|
||||
@@ -1656,19 +1654,18 @@ bpf_hash_map_seq_find_next(struct bpf_iter_seq_hash_map_info *info,
|
||||
|
||||
/* not found, unlock and go to the next bucket */
|
||||
b = &htab->buckets[bucket_id++];
|
||||
htab_unlock_bucket(htab, b, flags);
|
||||
rcu_read_unlock();
|
||||
skip_elems = 0;
|
||||
}
|
||||
|
||||
for (i = bucket_id; i < htab->n_buckets; i++) {
|
||||
b = &htab->buckets[i];
|
||||
flags = htab_lock_bucket(htab, b);
|
||||
rcu_read_lock();
|
||||
|
||||
count = 0;
|
||||
head = &b->head;
|
||||
hlist_nulls_for_each_entry_rcu(elem, n, head, hash_node) {
|
||||
if (count >= skip_elems) {
|
||||
info->flags = flags;
|
||||
info->bucket_id = i;
|
||||
info->skip_elems = count;
|
||||
return elem;
|
||||
@@ -1676,7 +1673,7 @@ bpf_hash_map_seq_find_next(struct bpf_iter_seq_hash_map_info *info,
|
||||
count++;
|
||||
}
|
||||
|
||||
htab_unlock_bucket(htab, b, flags);
|
||||
rcu_read_unlock();
|
||||
skip_elems = 0;
|
||||
}
|
||||
|
||||
@@ -1754,14 +1751,10 @@ static int bpf_hash_map_seq_show(struct seq_file *seq, void *v)
|
||||
|
||||
static void bpf_hash_map_seq_stop(struct seq_file *seq, void *v)
|
||||
{
|
||||
struct bpf_iter_seq_hash_map_info *info = seq->private;
|
||||
|
||||
if (!v)
|
||||
(void)__bpf_hash_map_seq_show(seq, NULL);
|
||||
else
|
||||
htab_unlock_bucket(info->htab,
|
||||
&info->htab->buckets[info->bucket_id],
|
||||
info->flags);
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
static int bpf_iter_init_hash_map(void *priv_data,
|
||||
@@ -1810,6 +1803,7 @@ static const struct bpf_iter_seq_info iter_seq_info = {
|
||||
|
||||
static int htab_map_btf_id;
|
||||
const struct bpf_map_ops htab_map_ops = {
|
||||
.map_meta_equal = bpf_map_meta_equal,
|
||||
.map_alloc_check = htab_map_alloc_check,
|
||||
.map_alloc = htab_map_alloc,
|
||||
.map_free = htab_map_free,
|
||||
@@ -1827,6 +1821,7 @@ const struct bpf_map_ops htab_map_ops = {
|
||||
|
||||
static int htab_lru_map_btf_id;
|
||||
const struct bpf_map_ops htab_lru_map_ops = {
|
||||
.map_meta_equal = bpf_map_meta_equal,
|
||||
.map_alloc_check = htab_map_alloc_check,
|
||||
.map_alloc = htab_map_alloc,
|
||||
.map_free = htab_map_free,
|
||||
@@ -1947,6 +1942,7 @@ static void htab_percpu_map_seq_show_elem(struct bpf_map *map, void *key,
|
||||
|
||||
static int htab_percpu_map_btf_id;
|
||||
const struct bpf_map_ops htab_percpu_map_ops = {
|
||||
.map_meta_equal = bpf_map_meta_equal,
|
||||
.map_alloc_check = htab_map_alloc_check,
|
||||
.map_alloc = htab_map_alloc,
|
||||
.map_free = htab_map_free,
|
||||
@@ -1963,6 +1959,7 @@ const struct bpf_map_ops htab_percpu_map_ops = {
|
||||
|
||||
static int htab_lru_percpu_map_btf_id;
|
||||
const struct bpf_map_ops htab_lru_percpu_map_ops = {
|
||||
.map_meta_equal = bpf_map_meta_equal,
|
||||
.map_alloc_check = htab_map_alloc_check,
|
||||
.map_alloc = htab_map_alloc,
|
||||
.map_free = htab_map_free,
|
||||
@@ -2073,7 +2070,7 @@ static void *htab_of_map_lookup_elem(struct bpf_map *map, void *key)
|
||||
return READ_ONCE(*inner_map);
|
||||
}
|
||||
|
||||
static u32 htab_of_map_gen_lookup(struct bpf_map *map,
|
||||
static int htab_of_map_gen_lookup(struct bpf_map *map,
|
||||
struct bpf_insn *insn_buf)
|
||||
{
|
||||
struct bpf_insn *insn = insn_buf;
|
||||
|
||||
@@ -601,6 +601,56 @@ const struct bpf_func_proto bpf_event_output_data_proto = {
|
||||
.arg5_type = ARG_CONST_SIZE_OR_ZERO,
|
||||
};
|
||||
|
||||
BPF_CALL_3(bpf_copy_from_user, void *, dst, u32, size,
|
||||
const void __user *, user_ptr)
|
||||
{
|
||||
int ret = copy_from_user(dst, user_ptr, size);
|
||||
|
||||
if (unlikely(ret)) {
|
||||
memset(dst, 0, size);
|
||||
ret = -EFAULT;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
const struct bpf_func_proto bpf_copy_from_user_proto = {
|
||||
.func = bpf_copy_from_user,
|
||||
.gpl_only = false,
|
||||
.ret_type = RET_INTEGER,
|
||||
.arg1_type = ARG_PTR_TO_UNINIT_MEM,
|
||||
.arg2_type = ARG_CONST_SIZE_OR_ZERO,
|
||||
.arg3_type = ARG_ANYTHING,
|
||||
};
|
||||
|
||||
BPF_CALL_2(bpf_per_cpu_ptr, const void *, ptr, u32, cpu)
|
||||
{
|
||||
if (cpu >= nr_cpu_ids)
|
||||
return (unsigned long)NULL;
|
||||
|
||||
return (unsigned long)per_cpu_ptr((const void __percpu *)ptr, cpu);
|
||||
}
|
||||
|
||||
const struct bpf_func_proto bpf_per_cpu_ptr_proto = {
|
||||
.func = bpf_per_cpu_ptr,
|
||||
.gpl_only = false,
|
||||
.ret_type = RET_PTR_TO_MEM_OR_BTF_ID_OR_NULL,
|
||||
.arg1_type = ARG_PTR_TO_PERCPU_BTF_ID,
|
||||
.arg2_type = ARG_ANYTHING,
|
||||
};
|
||||
|
||||
BPF_CALL_1(bpf_this_cpu_ptr, const void *, percpu_ptr)
|
||||
{
|
||||
return (unsigned long)this_cpu_ptr((const void __percpu *)percpu_ptr);
|
||||
}
|
||||
|
||||
const struct bpf_func_proto bpf_this_cpu_ptr_proto = {
|
||||
.func = bpf_this_cpu_ptr,
|
||||
.gpl_only = false,
|
||||
.ret_type = RET_PTR_TO_MEM_OR_BTF_ID,
|
||||
.arg1_type = ARG_PTR_TO_PERCPU_BTF_ID,
|
||||
};
|
||||
|
||||
const struct bpf_func_proto bpf_get_current_task_proto __weak;
|
||||
const struct bpf_func_proto bpf_probe_read_user_proto __weak;
|
||||
const struct bpf_func_proto bpf_probe_read_user_str_proto __weak;
|
||||
@@ -661,8 +711,16 @@ bpf_base_func_proto(enum bpf_func_id func_id)
|
||||
if (!perfmon_capable())
|
||||
return NULL;
|
||||
return bpf_get_trace_printk_proto();
|
||||
case BPF_FUNC_snprintf_btf:
|
||||
if (!perfmon_capable())
|
||||
return NULL;
|
||||
return &bpf_snprintf_btf_proto;
|
||||
case BPF_FUNC_jiffies64:
|
||||
return &bpf_jiffies64_proto;
|
||||
case BPF_FUNC_bpf_per_cpu_ptr:
|
||||
return &bpf_per_cpu_ptr_proto;
|
||||
case BPF_FUNC_bpf_this_cpu_ptr:
|
||||
return &bpf_this_cpu_ptr_proto;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
+116
-4
@@ -20,6 +20,7 @@
|
||||
#include <linux/filter.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/bpf_trace.h>
|
||||
#include "preload/bpf_preload.h"
|
||||
|
||||
enum bpf_type {
|
||||
BPF_TYPE_UNSPEC = 0,
|
||||
@@ -226,10 +227,12 @@ static void *map_seq_next(struct seq_file *m, void *v, loff_t *pos)
|
||||
else
|
||||
prev_key = key;
|
||||
|
||||
rcu_read_lock();
|
||||
if (map->ops->map_get_next_key(map, prev_key, key)) {
|
||||
map_iter(m)->done = true;
|
||||
return NULL;
|
||||
key = NULL;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
return key;
|
||||
}
|
||||
|
||||
@@ -369,9 +372,10 @@ static struct dentry *
|
||||
bpf_lookup(struct inode *dir, struct dentry *dentry, unsigned flags)
|
||||
{
|
||||
/* Dots in names (e.g. "/sys/fs/bpf/foo.bar") are reserved for future
|
||||
* extensions.
|
||||
* extensions. That allows popoulate_bpffs() create special files.
|
||||
*/
|
||||
if (strchr(dentry->d_name.name, '.'))
|
||||
if ((dir->i_mode & S_IALLUGO) &&
|
||||
strchr(dentry->d_name.name, '.'))
|
||||
return ERR_PTR(-EPERM);
|
||||
|
||||
return simple_lookup(dir, dentry, flags);
|
||||
@@ -409,6 +413,27 @@ static const struct inode_operations bpf_dir_iops = {
|
||||
.unlink = simple_unlink,
|
||||
};
|
||||
|
||||
/* pin iterator link into bpffs */
|
||||
static int bpf_iter_link_pin_kernel(struct dentry *parent,
|
||||
const char *name, struct bpf_link *link)
|
||||
{
|
||||
umode_t mode = S_IFREG | S_IRUSR;
|
||||
struct dentry *dentry;
|
||||
int ret;
|
||||
|
||||
inode_lock(parent->d_inode);
|
||||
dentry = lookup_one_len(name, parent, strlen(name));
|
||||
if (IS_ERR(dentry)) {
|
||||
inode_unlock(parent->d_inode);
|
||||
return PTR_ERR(dentry);
|
||||
}
|
||||
ret = bpf_mkobj_ops(dentry, mode, link, &bpf_link_iops,
|
||||
&bpf_iter_fops);
|
||||
dput(dentry);
|
||||
inode_unlock(parent->d_inode);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bpf_obj_do_pin(const char __user *pathname, void *raw,
|
||||
enum bpf_type type)
|
||||
{
|
||||
@@ -638,6 +663,91 @@ static int bpf_parse_param(struct fs_context *fc, struct fs_parameter *param)
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct bpf_preload_ops *bpf_preload_ops;
|
||||
EXPORT_SYMBOL_GPL(bpf_preload_ops);
|
||||
|
||||
static bool bpf_preload_mod_get(void)
|
||||
{
|
||||
/* If bpf_preload.ko wasn't loaded earlier then load it now.
|
||||
* When bpf_preload is built into vmlinux the module's __init
|
||||
* function will populate it.
|
||||
*/
|
||||
if (!bpf_preload_ops) {
|
||||
request_module("bpf_preload");
|
||||
if (!bpf_preload_ops)
|
||||
return false;
|
||||
}
|
||||
/* And grab the reference, so the module doesn't disappear while the
|
||||
* kernel is interacting with the kernel module and its UMD.
|
||||
*/
|
||||
if (!try_module_get(bpf_preload_ops->owner)) {
|
||||
pr_err("bpf_preload module get failed.\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void bpf_preload_mod_put(void)
|
||||
{
|
||||
if (bpf_preload_ops)
|
||||
/* now user can "rmmod bpf_preload" if necessary */
|
||||
module_put(bpf_preload_ops->owner);
|
||||
}
|
||||
|
||||
static DEFINE_MUTEX(bpf_preload_lock);
|
||||
|
||||
static int populate_bpffs(struct dentry *parent)
|
||||
{
|
||||
struct bpf_preload_info objs[BPF_PRELOAD_LINKS] = {};
|
||||
struct bpf_link *links[BPF_PRELOAD_LINKS] = {};
|
||||
int err = 0, i;
|
||||
|
||||
/* grab the mutex to make sure the kernel interactions with bpf_preload
|
||||
* UMD are serialized
|
||||
*/
|
||||
mutex_lock(&bpf_preload_lock);
|
||||
|
||||
/* if bpf_preload.ko wasn't built into vmlinux then load it */
|
||||
if (!bpf_preload_mod_get())
|
||||
goto out;
|
||||
|
||||
if (!bpf_preload_ops->info.tgid) {
|
||||
/* preload() will start UMD that will load BPF iterator programs */
|
||||
err = bpf_preload_ops->preload(objs);
|
||||
if (err)
|
||||
goto out_put;
|
||||
for (i = 0; i < BPF_PRELOAD_LINKS; i++) {
|
||||
links[i] = bpf_link_by_id(objs[i].link_id);
|
||||
if (IS_ERR(links[i])) {
|
||||
err = PTR_ERR(links[i]);
|
||||
goto out_put;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < BPF_PRELOAD_LINKS; i++) {
|
||||
err = bpf_iter_link_pin_kernel(parent,
|
||||
objs[i].link_name, links[i]);
|
||||
if (err)
|
||||
goto out_put;
|
||||
/* do not unlink successfully pinned links even
|
||||
* if later link fails to pin
|
||||
*/
|
||||
links[i] = NULL;
|
||||
}
|
||||
/* finish() will tell UMD process to exit */
|
||||
err = bpf_preload_ops->finish();
|
||||
if (err)
|
||||
goto out_put;
|
||||
}
|
||||
out_put:
|
||||
bpf_preload_mod_put();
|
||||
out:
|
||||
mutex_unlock(&bpf_preload_lock);
|
||||
for (i = 0; i < BPF_PRELOAD_LINKS && err; i++)
|
||||
if (!IS_ERR_OR_NULL(links[i]))
|
||||
bpf_link_put(links[i]);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int bpf_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
{
|
||||
static const struct tree_descr bpf_rfiles[] = { { "" } };
|
||||
@@ -654,8 +764,8 @@ static int bpf_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
inode = sb->s_root->d_inode;
|
||||
inode->i_op = &bpf_dir_iops;
|
||||
inode->i_mode &= ~S_IALLUGO;
|
||||
populate_bpffs(sb->s_root);
|
||||
inode->i_mode |= S_ISVTX | opts->mode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -705,6 +815,8 @@ static int __init bpf_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_init(&bpf_preload_lock);
|
||||
|
||||
ret = sysfs_create_mount_point(fs_kobj, "bpf");
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -732,6 +732,7 @@ static int trie_check_btf(const struct bpf_map *map,
|
||||
|
||||
static int trie_map_btf_id;
|
||||
const struct bpf_map_ops trie_map_ops = {
|
||||
.map_meta_equal = bpf_map_meta_equal,
|
||||
.map_alloc = trie_alloc,
|
||||
.map_free = trie_free,
|
||||
.map_get_next_key = trie_get_next_key,
|
||||
|
||||
+9
-15
@@ -17,23 +17,17 @@ struct bpf_map *bpf_map_meta_alloc(int inner_map_ufd)
|
||||
if (IS_ERR(inner_map))
|
||||
return inner_map;
|
||||
|
||||
/* prog_array->aux->{type,jited} is a runtime binding.
|
||||
* Doing static check alone in the verifier is not enough.
|
||||
*/
|
||||
if (inner_map->map_type == BPF_MAP_TYPE_PROG_ARRAY ||
|
||||
inner_map->map_type == BPF_MAP_TYPE_CGROUP_STORAGE ||
|
||||
inner_map->map_type == BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE ||
|
||||
inner_map->map_type == BPF_MAP_TYPE_STRUCT_OPS) {
|
||||
fdput(f);
|
||||
return ERR_PTR(-ENOTSUPP);
|
||||
}
|
||||
|
||||
/* Does not support >1 level map-in-map */
|
||||
if (inner_map->inner_map_meta) {
|
||||
fdput(f);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
if (!inner_map->ops->map_meta_equal) {
|
||||
fdput(f);
|
||||
return ERR_PTR(-ENOTSUPP);
|
||||
}
|
||||
|
||||
if (map_value_has_spin_lock(inner_map)) {
|
||||
fdput(f);
|
||||
return ERR_PTR(-ENOTSUPP);
|
||||
@@ -81,15 +75,14 @@ bool bpf_map_meta_equal(const struct bpf_map *meta0,
|
||||
return meta0->map_type == meta1->map_type &&
|
||||
meta0->key_size == meta1->key_size &&
|
||||
meta0->value_size == meta1->value_size &&
|
||||
meta0->map_flags == meta1->map_flags &&
|
||||
meta0->max_entries == meta1->max_entries;
|
||||
meta0->map_flags == meta1->map_flags;
|
||||
}
|
||||
|
||||
void *bpf_map_fd_get_ptr(struct bpf_map *map,
|
||||
struct file *map_file /* not used */,
|
||||
int ufd)
|
||||
{
|
||||
struct bpf_map *inner_map;
|
||||
struct bpf_map *inner_map, *inner_map_meta;
|
||||
struct fd f;
|
||||
|
||||
f = fdget(ufd);
|
||||
@@ -97,7 +90,8 @@ void *bpf_map_fd_get_ptr(struct bpf_map *map,
|
||||
if (IS_ERR(inner_map))
|
||||
return inner_map;
|
||||
|
||||
if (bpf_map_meta_equal(map->inner_map_meta, inner_map))
|
||||
inner_map_meta = map->inner_map_meta;
|
||||
if (inner_map_meta->ops->map_meta_equal(inner_map_meta, inner_map))
|
||||
bpf_map_inc(inner_map);
|
||||
else
|
||||
inner_map = ERR_PTR(-EINVAL);
|
||||
|
||||
@@ -11,8 +11,6 @@ struct bpf_map;
|
||||
|
||||
struct bpf_map *bpf_map_meta_alloc(int inner_map_ufd);
|
||||
void bpf_map_meta_free(struct bpf_map *map_meta);
|
||||
bool bpf_map_meta_equal(const struct bpf_map *meta0,
|
||||
const struct bpf_map *meta1);
|
||||
void *bpf_map_fd_get_ptr(struct bpf_map *map, struct file *map_file,
|
||||
int ufd);
|
||||
void bpf_map_fd_put_ptr(void *ptr);
|
||||
|
||||
@@ -149,6 +149,19 @@ static void bpf_iter_detach_map(struct bpf_iter_aux_info *aux)
|
||||
bpf_map_put_with_uref(aux->map);
|
||||
}
|
||||
|
||||
void bpf_iter_map_show_fdinfo(const struct bpf_iter_aux_info *aux,
|
||||
struct seq_file *seq)
|
||||
{
|
||||
seq_printf(seq, "map_id:\t%u\n", aux->map->id);
|
||||
}
|
||||
|
||||
int bpf_iter_map_fill_link_info(const struct bpf_iter_aux_info *aux,
|
||||
struct bpf_link_info *info)
|
||||
{
|
||||
info->iter.map.map_id = aux->map->id;
|
||||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_BPF_ITER_FUNC(bpf_map_elem, struct bpf_iter_meta *meta,
|
||||
struct bpf_map *map, void *key, void *value)
|
||||
|
||||
@@ -156,6 +169,8 @@ static const struct bpf_iter_reg bpf_map_elem_reg_info = {
|
||||
.target = "bpf_map_elem",
|
||||
.attach_target = bpf_iter_attach_map,
|
||||
.detach_target = bpf_iter_detach_map,
|
||||
.show_fdinfo = bpf_iter_map_show_fdinfo,
|
||||
.fill_link_info = bpf_iter_map_fill_link_info,
|
||||
.ctx_arg_info_size = 2,
|
||||
.ctx_arg_info = {
|
||||
{ offsetof(struct bpf_iter__bpf_map_elem, key),
|
||||
|
||||
@@ -17,6 +17,8 @@ int pcpu_freelist_init(struct pcpu_freelist *s)
|
||||
raw_spin_lock_init(&head->lock);
|
||||
head->first = NULL;
|
||||
}
|
||||
raw_spin_lock_init(&s->extralist.lock);
|
||||
s->extralist.first = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -40,12 +42,50 @@ static inline void ___pcpu_freelist_push(struct pcpu_freelist_head *head,
|
||||
raw_spin_unlock(&head->lock);
|
||||
}
|
||||
|
||||
static inline bool pcpu_freelist_try_push_extra(struct pcpu_freelist *s,
|
||||
struct pcpu_freelist_node *node)
|
||||
{
|
||||
if (!raw_spin_trylock(&s->extralist.lock))
|
||||
return false;
|
||||
|
||||
pcpu_freelist_push_node(&s->extralist, node);
|
||||
raw_spin_unlock(&s->extralist.lock);
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void ___pcpu_freelist_push_nmi(struct pcpu_freelist *s,
|
||||
struct pcpu_freelist_node *node)
|
||||
{
|
||||
int cpu, orig_cpu;
|
||||
|
||||
orig_cpu = cpu = raw_smp_processor_id();
|
||||
while (1) {
|
||||
struct pcpu_freelist_head *head;
|
||||
|
||||
head = per_cpu_ptr(s->freelist, cpu);
|
||||
if (raw_spin_trylock(&head->lock)) {
|
||||
pcpu_freelist_push_node(head, node);
|
||||
raw_spin_unlock(&head->lock);
|
||||
return;
|
||||
}
|
||||
cpu = cpumask_next(cpu, cpu_possible_mask);
|
||||
if (cpu >= nr_cpu_ids)
|
||||
cpu = 0;
|
||||
|
||||
/* cannot lock any per cpu lock, try extralist */
|
||||
if (cpu == orig_cpu &&
|
||||
pcpu_freelist_try_push_extra(s, node))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void __pcpu_freelist_push(struct pcpu_freelist *s,
|
||||
struct pcpu_freelist_node *node)
|
||||
{
|
||||
struct pcpu_freelist_head *head = this_cpu_ptr(s->freelist);
|
||||
|
||||
___pcpu_freelist_push(head, node);
|
||||
if (in_nmi())
|
||||
___pcpu_freelist_push_nmi(s, node);
|
||||
else
|
||||
___pcpu_freelist_push(this_cpu_ptr(s->freelist), node);
|
||||
}
|
||||
|
||||
void pcpu_freelist_push(struct pcpu_freelist *s,
|
||||
@@ -81,7 +121,7 @@ again:
|
||||
}
|
||||
}
|
||||
|
||||
struct pcpu_freelist_node *__pcpu_freelist_pop(struct pcpu_freelist *s)
|
||||
static struct pcpu_freelist_node *___pcpu_freelist_pop(struct pcpu_freelist *s)
|
||||
{
|
||||
struct pcpu_freelist_head *head;
|
||||
struct pcpu_freelist_node *node;
|
||||
@@ -102,8 +142,59 @@ struct pcpu_freelist_node *__pcpu_freelist_pop(struct pcpu_freelist *s)
|
||||
if (cpu >= nr_cpu_ids)
|
||||
cpu = 0;
|
||||
if (cpu == orig_cpu)
|
||||
return NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
/* per cpu lists are all empty, try extralist */
|
||||
raw_spin_lock(&s->extralist.lock);
|
||||
node = s->extralist.first;
|
||||
if (node)
|
||||
s->extralist.first = node->next;
|
||||
raw_spin_unlock(&s->extralist.lock);
|
||||
return node;
|
||||
}
|
||||
|
||||
static struct pcpu_freelist_node *
|
||||
___pcpu_freelist_pop_nmi(struct pcpu_freelist *s)
|
||||
{
|
||||
struct pcpu_freelist_head *head;
|
||||
struct pcpu_freelist_node *node;
|
||||
int orig_cpu, cpu;
|
||||
|
||||
orig_cpu = cpu = raw_smp_processor_id();
|
||||
while (1) {
|
||||
head = per_cpu_ptr(s->freelist, cpu);
|
||||
if (raw_spin_trylock(&head->lock)) {
|
||||
node = head->first;
|
||||
if (node) {
|
||||
head->first = node->next;
|
||||
raw_spin_unlock(&head->lock);
|
||||
return node;
|
||||
}
|
||||
raw_spin_unlock(&head->lock);
|
||||
}
|
||||
cpu = cpumask_next(cpu, cpu_possible_mask);
|
||||
if (cpu >= nr_cpu_ids)
|
||||
cpu = 0;
|
||||
if (cpu == orig_cpu)
|
||||
break;
|
||||
}
|
||||
|
||||
/* cannot pop from per cpu lists, try extralist */
|
||||
if (!raw_spin_trylock(&s->extralist.lock))
|
||||
return NULL;
|
||||
node = s->extralist.first;
|
||||
if (node)
|
||||
s->extralist.first = node->next;
|
||||
raw_spin_unlock(&s->extralist.lock);
|
||||
return node;
|
||||
}
|
||||
|
||||
struct pcpu_freelist_node *__pcpu_freelist_pop(struct pcpu_freelist *s)
|
||||
{
|
||||
if (in_nmi())
|
||||
return ___pcpu_freelist_pop_nmi(s);
|
||||
return ___pcpu_freelist_pop(s);
|
||||
}
|
||||
|
||||
struct pcpu_freelist_node *pcpu_freelist_pop(struct pcpu_freelist *s)
|
||||
|
||||
@@ -13,6 +13,7 @@ struct pcpu_freelist_head {
|
||||
|
||||
struct pcpu_freelist {
|
||||
struct pcpu_freelist_head __percpu *freelist;
|
||||
struct pcpu_freelist_head extralist;
|
||||
};
|
||||
|
||||
struct pcpu_freelist_node {
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
/FEATURE-DUMP.libbpf
|
||||
/bpf_helper_defs.h
|
||||
/feature
|
||||
/bpf_preload_umd
|
||||
@@ -0,0 +1,26 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
config USERMODE_DRIVER
|
||||
bool
|
||||
default n
|
||||
|
||||
menuconfig BPF_PRELOAD
|
||||
bool "Preload BPF file system with kernel specific program and map iterators"
|
||||
depends on BPF
|
||||
# The dependency on !COMPILE_TEST prevents it from being enabled
|
||||
# in allmodconfig or allyesconfig configurations
|
||||
depends on !COMPILE_TEST
|
||||
select USERMODE_DRIVER
|
||||
help
|
||||
This builds kernel module with several embedded BPF programs that are
|
||||
pinned into BPF FS mount point as human readable files that are
|
||||
useful in debugging and introspection of BPF programs and maps.
|
||||
|
||||
if BPF_PRELOAD
|
||||
config BPF_PRELOAD_UMD
|
||||
tristate "bpf_preload kernel module with user mode driver"
|
||||
depends on CC_CAN_LINK
|
||||
depends on m || CC_CAN_LINK_STATIC
|
||||
default m
|
||||
help
|
||||
This builds bpf_preload kernel module with embedded user mode driver.
|
||||
endif
|
||||
@@ -0,0 +1,25 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
LIBBPF_SRCS = $(srctree)/tools/lib/bpf/
|
||||
LIBBPF_A = $(obj)/libbpf.a
|
||||
LIBBPF_OUT = $(abspath $(obj))
|
||||
|
||||
$(LIBBPF_A):
|
||||
$(Q)$(MAKE) -C $(LIBBPF_SRCS) OUTPUT=$(LIBBPF_OUT)/ $(LIBBPF_OUT)/libbpf.a
|
||||
|
||||
userccflags += -I $(srctree)/tools/include/ -I $(srctree)/tools/include/uapi \
|
||||
-I $(srctree)/tools/lib/ -Wno-unused-result
|
||||
|
||||
userprogs := bpf_preload_umd
|
||||
|
||||
clean-files := $(userprogs) bpf_helper_defs.h FEATURE-DUMP.libbpf staticobjs/ feature/
|
||||
|
||||
bpf_preload_umd-objs := iterators/iterators.o
|
||||
bpf_preload_umd-userldlibs := $(LIBBPF_A) -lelf -lz
|
||||
|
||||
$(obj)/bpf_preload_umd: $(LIBBPF_A)
|
||||
|
||||
$(obj)/bpf_preload_umd_blob.o: $(obj)/bpf_preload_umd
|
||||
|
||||
obj-$(CONFIG_BPF_PRELOAD_UMD) += bpf_preload.o
|
||||
bpf_preload-objs += bpf_preload_kern.o bpf_preload_umd_blob.o
|
||||
@@ -0,0 +1,16 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef _BPF_PRELOAD_H
|
||||
#define _BPF_PRELOAD_H
|
||||
|
||||
#include <linux/usermode_driver.h>
|
||||
#include "iterators/bpf_preload_common.h"
|
||||
|
||||
struct bpf_preload_ops {
|
||||
struct umd_info info;
|
||||
int (*preload)(struct bpf_preload_info *);
|
||||
int (*finish)(void);
|
||||
struct module *owner;
|
||||
};
|
||||
extern struct bpf_preload_ops *bpf_preload_ops;
|
||||
#define BPF_PRELOAD_LINKS 2
|
||||
#endif
|
||||
@@ -0,0 +1,91 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pid.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/sched/signal.h>
|
||||
#include "bpf_preload.h"
|
||||
|
||||
extern char bpf_preload_umd_start;
|
||||
extern char bpf_preload_umd_end;
|
||||
|
||||
static int preload(struct bpf_preload_info *obj);
|
||||
static int finish(void);
|
||||
|
||||
static struct bpf_preload_ops umd_ops = {
|
||||
.info.driver_name = "bpf_preload",
|
||||
.preload = preload,
|
||||
.finish = finish,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int preload(struct bpf_preload_info *obj)
|
||||
{
|
||||
int magic = BPF_PRELOAD_START;
|
||||
loff_t pos = 0;
|
||||
int i, err;
|
||||
ssize_t n;
|
||||
|
||||
err = fork_usermode_driver(&umd_ops.info);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* send the start magic to let UMD proceed with loading BPF progs */
|
||||
n = kernel_write(umd_ops.info.pipe_to_umh,
|
||||
&magic, sizeof(magic), &pos);
|
||||
if (n != sizeof(magic))
|
||||
return -EPIPE;
|
||||
|
||||
/* receive bpf_link IDs and names from UMD */
|
||||
pos = 0;
|
||||
for (i = 0; i < BPF_PRELOAD_LINKS; i++) {
|
||||
n = kernel_read(umd_ops.info.pipe_from_umh,
|
||||
&obj[i], sizeof(*obj), &pos);
|
||||
if (n != sizeof(*obj))
|
||||
return -EPIPE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int finish(void)
|
||||
{
|
||||
int magic = BPF_PRELOAD_END;
|
||||
struct pid *tgid;
|
||||
loff_t pos = 0;
|
||||
ssize_t n;
|
||||
|
||||
/* send the last magic to UMD. It will do a normal exit. */
|
||||
n = kernel_write(umd_ops.info.pipe_to_umh,
|
||||
&magic, sizeof(magic), &pos);
|
||||
if (n != sizeof(magic))
|
||||
return -EPIPE;
|
||||
tgid = umd_ops.info.tgid;
|
||||
wait_event(tgid->wait_pidfd, thread_group_exited(tgid));
|
||||
umd_ops.info.tgid = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init load_umd(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = umd_load_blob(&umd_ops.info, &bpf_preload_umd_start,
|
||||
&bpf_preload_umd_end - &bpf_preload_umd_start);
|
||||
if (err)
|
||||
return err;
|
||||
bpf_preload_ops = &umd_ops;
|
||||
return err;
|
||||
}
|
||||
|
||||
static void __exit fini_umd(void)
|
||||
{
|
||||
bpf_preload_ops = NULL;
|
||||
/* kill UMD in case it's still there due to earlier error */
|
||||
kill_pid(umd_ops.info.tgid, SIGKILL, 1);
|
||||
umd_ops.info.tgid = NULL;
|
||||
umd_unload_blob(&umd_ops.info);
|
||||
}
|
||||
late_initcall(load_umd);
|
||||
module_exit(fini_umd);
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -0,0 +1,7 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
.section .init.rodata, "a"
|
||||
.global bpf_preload_umd_start
|
||||
bpf_preload_umd_start:
|
||||
.incbin "kernel/bpf/preload/bpf_preload_umd"
|
||||
.global bpf_preload_umd_end
|
||||
bpf_preload_umd_end:
|
||||
@@ -0,0 +1,2 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
/.output
|
||||
@@ -0,0 +1,57 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
OUTPUT := .output
|
||||
CLANG ?= clang
|
||||
LLC ?= llc
|
||||
LLVM_STRIP ?= llvm-strip
|
||||
DEFAULT_BPFTOOL := $(OUTPUT)/sbin/bpftool
|
||||
BPFTOOL ?= $(DEFAULT_BPFTOOL)
|
||||
LIBBPF_SRC := $(abspath ../../../../tools/lib/bpf)
|
||||
BPFOBJ := $(OUTPUT)/libbpf.a
|
||||
BPF_INCLUDE := $(OUTPUT)
|
||||
INCLUDES := -I$(OUTPUT) -I$(BPF_INCLUDE) -I$(abspath ../../../../tools/lib) \
|
||||
-I$(abspath ../../../../tools/include/uapi)
|
||||
CFLAGS := -g -Wall
|
||||
|
||||
abs_out := $(abspath $(OUTPUT))
|
||||
ifeq ($(V),1)
|
||||
Q =
|
||||
msg =
|
||||
else
|
||||
Q = @
|
||||
msg = @printf ' %-8s %s%s\n' "$(1)" "$(notdir $(2))" "$(if $(3), $(3))";
|
||||
MAKEFLAGS += --no-print-directory
|
||||
submake_extras := feature_display=0
|
||||
endif
|
||||
|
||||
.DELETE_ON_ERROR:
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: iterators.skel.h
|
||||
|
||||
clean:
|
||||
$(call msg,CLEAN)
|
||||
$(Q)rm -rf $(OUTPUT) iterators
|
||||
|
||||
iterators.skel.h: $(OUTPUT)/iterators.bpf.o | $(BPFTOOL)
|
||||
$(call msg,GEN-SKEL,$@)
|
||||
$(Q)$(BPFTOOL) gen skeleton $< > $@
|
||||
|
||||
|
||||
$(OUTPUT)/iterators.bpf.o: iterators.bpf.c $(BPFOBJ) | $(OUTPUT)
|
||||
$(call msg,BPF,$@)
|
||||
$(Q)$(CLANG) -g -O2 -target bpf $(INCLUDES) \
|
||||
-c $(filter %.c,$^) -o $@ && \
|
||||
$(LLVM_STRIP) -g $@
|
||||
|
||||
$(OUTPUT):
|
||||
$(call msg,MKDIR,$@)
|
||||
$(Q)mkdir -p $(OUTPUT)
|
||||
|
||||
$(BPFOBJ): $(wildcard $(LIBBPF_SRC)/*.[ch] $(LIBBPF_SRC)/Makefile) | $(OUTPUT)
|
||||
$(Q)$(MAKE) $(submake_extras) -C $(LIBBPF_SRC) \
|
||||
OUTPUT=$(abspath $(dir $@))/ $(abspath $@)
|
||||
|
||||
$(DEFAULT_BPFTOOL):
|
||||
$(Q)$(MAKE) $(submake_extras) -C ../../../../tools/bpf/bpftool \
|
||||
prefix= OUTPUT=$(abs_out)/ DESTDIR=$(abs_out) install
|
||||
@@ -0,0 +1,4 @@
|
||||
WARNING:
|
||||
If you change "iterators.bpf.c" do "make -j" in this directory to rebuild "iterators.skel.h".
|
||||
Make sure to have clang 10 installed.
|
||||
See Documentation/bpf/bpf_devel_QA.rst
|
||||
@@ -0,0 +1,13 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef _BPF_PRELOAD_COMMON_H
|
||||
#define _BPF_PRELOAD_COMMON_H
|
||||
|
||||
#define BPF_PRELOAD_START 0x5555
|
||||
#define BPF_PRELOAD_END 0xAAAA
|
||||
|
||||
struct bpf_preload_info {
|
||||
char link_name[16];
|
||||
int link_id;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,114 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2020 Facebook */
|
||||
#include <linux/bpf.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
#include <bpf/bpf_core_read.h>
|
||||
|
||||
#pragma clang attribute push (__attribute__((preserve_access_index)), apply_to = record)
|
||||
struct seq_file;
|
||||
struct bpf_iter_meta {
|
||||
struct seq_file *seq;
|
||||
__u64 session_id;
|
||||
__u64 seq_num;
|
||||
};
|
||||
|
||||
struct bpf_map {
|
||||
__u32 id;
|
||||
char name[16];
|
||||
__u32 max_entries;
|
||||
};
|
||||
|
||||
struct bpf_iter__bpf_map {
|
||||
struct bpf_iter_meta *meta;
|
||||
struct bpf_map *map;
|
||||
};
|
||||
|
||||
struct btf_type {
|
||||
__u32 name_off;
|
||||
};
|
||||
|
||||
struct btf_header {
|
||||
__u32 str_len;
|
||||
};
|
||||
|
||||
struct btf {
|
||||
const char *strings;
|
||||
struct btf_type **types;
|
||||
struct btf_header hdr;
|
||||
};
|
||||
|
||||
struct bpf_prog_aux {
|
||||
__u32 id;
|
||||
char name[16];
|
||||
const char *attach_func_name;
|
||||
struct bpf_prog *dst_prog;
|
||||
struct bpf_func_info *func_info;
|
||||
struct btf *btf;
|
||||
};
|
||||
|
||||
struct bpf_prog {
|
||||
struct bpf_prog_aux *aux;
|
||||
};
|
||||
|
||||
struct bpf_iter__bpf_prog {
|
||||
struct bpf_iter_meta *meta;
|
||||
struct bpf_prog *prog;
|
||||
};
|
||||
#pragma clang attribute pop
|
||||
|
||||
static const char *get_name(struct btf *btf, long btf_id, const char *fallback)
|
||||
{
|
||||
struct btf_type **types, *t;
|
||||
unsigned int name_off;
|
||||
const char *str;
|
||||
|
||||
if (!btf)
|
||||
return fallback;
|
||||
str = btf->strings;
|
||||
types = btf->types;
|
||||
bpf_probe_read_kernel(&t, sizeof(t), types + btf_id);
|
||||
name_off = BPF_CORE_READ(t, name_off);
|
||||
if (name_off >= btf->hdr.str_len)
|
||||
return fallback;
|
||||
return str + name_off;
|
||||
}
|
||||
|
||||
SEC("iter/bpf_map")
|
||||
int dump_bpf_map(struct bpf_iter__bpf_map *ctx)
|
||||
{
|
||||
struct seq_file *seq = ctx->meta->seq;
|
||||
__u64 seq_num = ctx->meta->seq_num;
|
||||
struct bpf_map *map = ctx->map;
|
||||
|
||||
if (!map)
|
||||
return 0;
|
||||
|
||||
if (seq_num == 0)
|
||||
BPF_SEQ_PRINTF(seq, " id name max_entries\n");
|
||||
|
||||
BPF_SEQ_PRINTF(seq, "%4u %-16s%6d\n", map->id, map->name, map->max_entries);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("iter/bpf_prog")
|
||||
int dump_bpf_prog(struct bpf_iter__bpf_prog *ctx)
|
||||
{
|
||||
struct seq_file *seq = ctx->meta->seq;
|
||||
__u64 seq_num = ctx->meta->seq_num;
|
||||
struct bpf_prog *prog = ctx->prog;
|
||||
struct bpf_prog_aux *aux;
|
||||
|
||||
if (!prog)
|
||||
return 0;
|
||||
|
||||
aux = prog->aux;
|
||||
if (seq_num == 0)
|
||||
BPF_SEQ_PRINTF(seq, " id name attached\n");
|
||||
|
||||
BPF_SEQ_PRINTF(seq, "%4u %-16s %s %s\n", aux->id,
|
||||
get_name(aux->btf, aux->func_info[0].type_id, aux->name),
|
||||
aux->attach_func_name, aux->dst_prog->aux->name);
|
||||
return 0;
|
||||
}
|
||||
char LICENSE[] SEC("license") = "GPL";
|
||||
@@ -0,0 +1,94 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2020 Facebook */
|
||||
#include <argp.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/resource.h>
|
||||
#include <bpf/libbpf.h>
|
||||
#include <bpf/bpf.h>
|
||||
#include <sys/mount.h>
|
||||
#include "iterators.skel.h"
|
||||
#include "bpf_preload_common.h"
|
||||
|
||||
int to_kernel = -1;
|
||||
int from_kernel = 0;
|
||||
|
||||
static int send_link_to_kernel(struct bpf_link *link, const char *link_name)
|
||||
{
|
||||
struct bpf_preload_info obj = {};
|
||||
struct bpf_link_info info = {};
|
||||
__u32 info_len = sizeof(info);
|
||||
int err;
|
||||
|
||||
err = bpf_obj_get_info_by_fd(bpf_link__fd(link), &info, &info_len);
|
||||
if (err)
|
||||
return err;
|
||||
obj.link_id = info.id;
|
||||
if (strlen(link_name) >= sizeof(obj.link_name))
|
||||
return -E2BIG;
|
||||
strcpy(obj.link_name, link_name);
|
||||
if (write(to_kernel, &obj, sizeof(obj)) != sizeof(obj))
|
||||
return -EPIPE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct rlimit rlim = { RLIM_INFINITY, RLIM_INFINITY };
|
||||
struct iterators_bpf *skel;
|
||||
int err, magic;
|
||||
int debug_fd;
|
||||
|
||||
debug_fd = open("/dev/console", O_WRONLY | O_NOCTTY | O_CLOEXEC);
|
||||
if (debug_fd < 0)
|
||||
return 1;
|
||||
to_kernel = dup(1);
|
||||
close(1);
|
||||
dup(debug_fd);
|
||||
/* now stdin and stderr point to /dev/console */
|
||||
|
||||
read(from_kernel, &magic, sizeof(magic));
|
||||
if (magic != BPF_PRELOAD_START) {
|
||||
printf("bad start magic %d\n", magic);
|
||||
return 1;
|
||||
}
|
||||
setrlimit(RLIMIT_MEMLOCK, &rlim);
|
||||
/* libbpf opens BPF object and loads it into the kernel */
|
||||
skel = iterators_bpf__open_and_load();
|
||||
if (!skel) {
|
||||
/* iterators.skel.h is little endian.
|
||||
* libbpf doesn't support automatic little->big conversion
|
||||
* of BPF bytecode yet.
|
||||
* The program load will fail in such case.
|
||||
*/
|
||||
printf("Failed load could be due to wrong endianness\n");
|
||||
return 1;
|
||||
}
|
||||
err = iterators_bpf__attach(skel);
|
||||
if (err)
|
||||
goto cleanup;
|
||||
|
||||
/* send two bpf_link IDs with names to the kernel */
|
||||
err = send_link_to_kernel(skel->links.dump_bpf_map, "maps.debug");
|
||||
if (err)
|
||||
goto cleanup;
|
||||
err = send_link_to_kernel(skel->links.dump_bpf_prog, "progs.debug");
|
||||
if (err)
|
||||
goto cleanup;
|
||||
|
||||
/* The kernel will proceed with pinnging the links in bpffs.
|
||||
* UMD will wait on read from pipe.
|
||||
*/
|
||||
read(from_kernel, &magic, sizeof(magic));
|
||||
if (magic != BPF_PRELOAD_END) {
|
||||
printf("bad final magic %d\n", magic);
|
||||
err = -EINVAL;
|
||||
}
|
||||
cleanup:
|
||||
iterators_bpf__destroy(skel);
|
||||
|
||||
return err != 0;
|
||||
}
|
||||
@@ -0,0 +1,412 @@
|
||||
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
|
||||
|
||||
/* THIS FILE IS AUTOGENERATED! */
|
||||
#ifndef __ITERATORS_BPF_SKEL_H__
|
||||
#define __ITERATORS_BPF_SKEL_H__
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <bpf/libbpf.h>
|
||||
|
||||
struct iterators_bpf {
|
||||
struct bpf_object_skeleton *skeleton;
|
||||
struct bpf_object *obj;
|
||||
struct {
|
||||
struct bpf_map *rodata;
|
||||
} maps;
|
||||
struct {
|
||||
struct bpf_program *dump_bpf_map;
|
||||
struct bpf_program *dump_bpf_prog;
|
||||
} progs;
|
||||
struct {
|
||||
struct bpf_link *dump_bpf_map;
|
||||
struct bpf_link *dump_bpf_prog;
|
||||
} links;
|
||||
struct iterators_bpf__rodata {
|
||||
char dump_bpf_map____fmt[35];
|
||||
char dump_bpf_map____fmt_1[14];
|
||||
char dump_bpf_prog____fmt[32];
|
||||
char dump_bpf_prog____fmt_2[17];
|
||||
} *rodata;
|
||||
};
|
||||
|
||||
static void
|
||||
iterators_bpf__destroy(struct iterators_bpf *obj)
|
||||
{
|
||||
if (!obj)
|
||||
return;
|
||||
if (obj->skeleton)
|
||||
bpf_object__destroy_skeleton(obj->skeleton);
|
||||
free(obj);
|
||||
}
|
||||
|
||||
static inline int
|
||||
iterators_bpf__create_skeleton(struct iterators_bpf *obj);
|
||||
|
||||
static inline struct iterators_bpf *
|
||||
iterators_bpf__open_opts(const struct bpf_object_open_opts *opts)
|
||||
{
|
||||
struct iterators_bpf *obj;
|
||||
|
||||
obj = (struct iterators_bpf *)calloc(1, sizeof(*obj));
|
||||
if (!obj)
|
||||
return NULL;
|
||||
if (iterators_bpf__create_skeleton(obj))
|
||||
goto err;
|
||||
if (bpf_object__open_skeleton(obj->skeleton, opts))
|
||||
goto err;
|
||||
|
||||
return obj;
|
||||
err:
|
||||
iterators_bpf__destroy(obj);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline struct iterators_bpf *
|
||||
iterators_bpf__open(void)
|
||||
{
|
||||
return iterators_bpf__open_opts(NULL);
|
||||
}
|
||||
|
||||
static inline int
|
||||
iterators_bpf__load(struct iterators_bpf *obj)
|
||||
{
|
||||
return bpf_object__load_skeleton(obj->skeleton);
|
||||
}
|
||||
|
||||
static inline struct iterators_bpf *
|
||||
iterators_bpf__open_and_load(void)
|
||||
{
|
||||
struct iterators_bpf *obj;
|
||||
|
||||
obj = iterators_bpf__open();
|
||||
if (!obj)
|
||||
return NULL;
|
||||
if (iterators_bpf__load(obj)) {
|
||||
iterators_bpf__destroy(obj);
|
||||
return NULL;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
static inline int
|
||||
iterators_bpf__attach(struct iterators_bpf *obj)
|
||||
{
|
||||
return bpf_object__attach_skeleton(obj->skeleton);
|
||||
}
|
||||
|
||||
static inline void
|
||||
iterators_bpf__detach(struct iterators_bpf *obj)
|
||||
{
|
||||
return bpf_object__detach_skeleton(obj->skeleton);
|
||||
}
|
||||
|
||||
static inline int
|
||||
iterators_bpf__create_skeleton(struct iterators_bpf *obj)
|
||||
{
|
||||
struct bpf_object_skeleton *s;
|
||||
|
||||
s = (struct bpf_object_skeleton *)calloc(1, sizeof(*s));
|
||||
if (!s)
|
||||
return -1;
|
||||
obj->skeleton = s;
|
||||
|
||||
s->sz = sizeof(*s);
|
||||
s->name = "iterators_bpf";
|
||||
s->obj = &obj->obj;
|
||||
|
||||
/* maps */
|
||||
s->map_cnt = 1;
|
||||
s->map_skel_sz = sizeof(*s->maps);
|
||||
s->maps = (struct bpf_map_skeleton *)calloc(s->map_cnt, s->map_skel_sz);
|
||||
if (!s->maps)
|
||||
goto err;
|
||||
|
||||
s->maps[0].name = "iterator.rodata";
|
||||
s->maps[0].map = &obj->maps.rodata;
|
||||
s->maps[0].mmaped = (void **)&obj->rodata;
|
||||
|
||||
/* programs */
|
||||
s->prog_cnt = 2;
|
||||
s->prog_skel_sz = sizeof(*s->progs);
|
||||
s->progs = (struct bpf_prog_skeleton *)calloc(s->prog_cnt, s->prog_skel_sz);
|
||||
if (!s->progs)
|
||||
goto err;
|
||||
|
||||
s->progs[0].name = "dump_bpf_map";
|
||||
s->progs[0].prog = &obj->progs.dump_bpf_map;
|
||||
s->progs[0].link = &obj->links.dump_bpf_map;
|
||||
|
||||
s->progs[1].name = "dump_bpf_prog";
|
||||
s->progs[1].prog = &obj->progs.dump_bpf_prog;
|
||||
s->progs[1].link = &obj->links.dump_bpf_prog;
|
||||
|
||||
s->data_sz = 7176;
|
||||
s->data = (void *)"\
|
||||
\x7f\x45\x4c\x46\x02\x01\x01\0\0\0\0\0\0\0\0\0\x01\0\xf7\0\x01\0\0\0\0\0\0\0\0\
|
||||
\0\0\0\0\0\0\0\0\0\0\0\x48\x18\0\0\0\0\0\0\0\0\0\0\x40\0\0\0\0\0\x40\0\x0f\0\
|
||||
\x0e\0\x79\x12\0\0\0\0\0\0\x79\x26\0\0\0\0\0\0\x79\x17\x08\0\0\0\0\0\x15\x07\
|
||||
\x1a\0\0\0\0\0\x79\x21\x10\0\0\0\0\0\x55\x01\x08\0\0\0\0\0\xbf\xa4\0\0\0\0\0\0\
|
||||
\x07\x04\0\0\xe8\xff\xff\xff\xbf\x61\0\0\0\0\0\0\x18\x02\0\0\0\0\0\0\0\0\0\0\0\
|
||||
\0\0\0\xb7\x03\0\0\x23\0\0\0\xb7\x05\0\0\0\0\0\0\x85\0\0\0\x7e\0\0\0\x61\x71\0\
|
||||
\0\0\0\0\0\x7b\x1a\xe8\xff\0\0\0\0\xb7\x01\0\0\x04\0\0\0\xbf\x72\0\0\0\0\0\0\
|
||||
\x0f\x12\0\0\0\0\0\0\x7b\x2a\xf0\xff\0\0\0\0\x61\x71\x14\0\0\0\0\0\x7b\x1a\xf8\
|
||||
\xff\0\0\0\0\xbf\xa4\0\0\0\0\0\0\x07\x04\0\0\xe8\xff\xff\xff\xbf\x61\0\0\0\0\0\
|
||||
\0\x18\x02\0\0\x23\0\0\0\0\0\0\0\0\0\0\0\xb7\x03\0\0\x0e\0\0\0\xb7\x05\0\0\x18\
|
||||
\0\0\0\x85\0\0\0\x7e\0\0\0\xb7\0\0\0\0\0\0\0\x95\0\0\0\0\0\0\0\x79\x12\0\0\0\0\
|
||||
\0\0\x79\x26\0\0\0\0\0\0\x79\x11\x08\0\0\0\0\0\x15\x01\x3b\0\0\0\0\0\x79\x17\0\
|
||||
\0\0\0\0\0\x79\x21\x10\0\0\0\0\0\x55\x01\x08\0\0\0\0\0\xbf\xa4\0\0\0\0\0\0\x07\
|
||||
\x04\0\0\xd0\xff\xff\xff\xbf\x61\0\0\0\0\0\0\x18\x02\0\0\x31\0\0\0\0\0\0\0\0\0\
|
||||
\0\0\xb7\x03\0\0\x20\0\0\0\xb7\x05\0\0\0\0\0\0\x85\0\0\0\x7e\0\0\0\x7b\x6a\xc8\
|
||||
\xff\0\0\0\0\x61\x71\0\0\0\0\0\0\x7b\x1a\xd0\xff\0\0\0\0\xb7\x03\0\0\x04\0\0\0\
|
||||
\xbf\x79\0\0\0\0\0\0\x0f\x39\0\0\0\0\0\0\x79\x71\x28\0\0\0\0\0\x79\x78\x30\0\0\
|
||||
\0\0\0\x15\x08\x18\0\0\0\0\0\xb7\x02\0\0\0\0\0\0\x0f\x21\0\0\0\0\0\0\x61\x11\
|
||||
\x04\0\0\0\0\0\x79\x83\x08\0\0\0\0\0\x67\x01\0\0\x03\0\0\0\x0f\x13\0\0\0\0\0\0\
|
||||
\x79\x86\0\0\0\0\0\0\xbf\xa1\0\0\0\0\0\0\x07\x01\0\0\xf8\xff\xff\xff\xb7\x02\0\
|
||||
\0\x08\0\0\0\x85\0\0\0\x71\0\0\0\xb7\x01\0\0\0\0\0\0\x79\xa3\xf8\xff\0\0\0\0\
|
||||
\x0f\x13\0\0\0\0\0\0\xbf\xa1\0\0\0\0\0\0\x07\x01\0\0\xf4\xff\xff\xff\xb7\x02\0\
|
||||
\0\x04\0\0\0\x85\0\0\0\x71\0\0\0\xb7\x03\0\0\x04\0\0\0\x61\xa1\xf4\xff\0\0\0\0\
|
||||
\x61\x82\x10\0\0\0\0\0\x3d\x21\x02\0\0\0\0\0\x0f\x16\0\0\0\0\0\0\xbf\x69\0\0\0\
|
||||
\0\0\0\x7b\x9a\xd8\xff\0\0\0\0\x79\x71\x18\0\0\0\0\0\x7b\x1a\xe0\xff\0\0\0\0\
|
||||
\x79\x71\x20\0\0\0\0\0\x79\x11\0\0\0\0\0\0\x0f\x31\0\0\0\0\0\0\x7b\x1a\xe8\xff\
|
||||
\0\0\0\0\xbf\xa4\0\0\0\0\0\0\x07\x04\0\0\xd0\xff\xff\xff\x79\xa1\xc8\xff\0\0\0\
|
||||
\0\x18\x02\0\0\x51\0\0\0\0\0\0\0\0\0\0\0\xb7\x03\0\0\x11\0\0\0\xb7\x05\0\0\x20\
|
||||
\0\0\0\x85\0\0\0\x7e\0\0\0\xb7\0\0\0\0\0\0\0\x95\0\0\0\0\0\0\0\x20\x20\x69\x64\
|
||||
\x20\x6e\x61\x6d\x65\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x6d\
|
||||
\x61\x78\x5f\x65\x6e\x74\x72\x69\x65\x73\x0a\0\x25\x34\x75\x20\x25\x2d\x31\x36\
|
||||
\x73\x25\x36\x64\x0a\0\x20\x20\x69\x64\x20\x6e\x61\x6d\x65\x20\x20\x20\x20\x20\
|
||||
\x20\x20\x20\x20\x20\x20\x20\x20\x61\x74\x74\x61\x63\x68\x65\x64\x0a\0\x25\x34\
|
||||
\x75\x20\x25\x2d\x31\x36\x73\x20\x25\x73\x20\x25\x73\x0a\0\x47\x50\x4c\0\x9f\
|
||||
\xeb\x01\0\x18\0\0\0\0\0\0\0\x1c\x04\0\0\x1c\x04\0\0\x09\x05\0\0\0\0\0\0\0\0\0\
|
||||
\x02\x02\0\0\0\x01\0\0\0\x02\0\0\x04\x10\0\0\0\x13\0\0\0\x03\0\0\0\0\0\0\0\x18\
|
||||
\0\0\0\x04\0\0\0\x40\0\0\0\0\0\0\0\0\0\0\x02\x08\0\0\0\0\0\0\0\0\0\0\x02\x0d\0\
|
||||
\0\0\0\0\0\0\x01\0\0\x0d\x06\0\0\0\x1c\0\0\0\x01\0\0\0\x20\0\0\0\0\0\0\x01\x04\
|
||||
\0\0\0\x20\0\0\x01\x24\0\0\0\x01\0\0\x0c\x05\0\0\0\xaf\0\0\0\x03\0\0\x04\x18\0\
|
||||
\0\0\xbd\0\0\0\x09\0\0\0\0\0\0\0\xc1\0\0\0\x0b\0\0\0\x40\0\0\0\xcc\0\0\0\x0b\0\
|
||||
\0\0\x80\0\0\0\0\0\0\0\0\0\0\x02\x0a\0\0\0\xd4\0\0\0\0\0\0\x07\0\0\0\0\xdd\0\0\
|
||||
\0\0\0\0\x08\x0c\0\0\0\xe3\0\0\0\0\0\0\x01\x08\0\0\0\x40\0\0\0\xa4\x01\0\0\x03\
|
||||
\0\0\x04\x18\0\0\0\xac\x01\0\0\x0e\0\0\0\0\0\0\0\xaf\x01\0\0\x11\0\0\0\x20\0\0\
|
||||
\0\xb4\x01\0\0\x0e\0\0\0\xa0\0\0\0\xc0\x01\0\0\0\0\0\x08\x0f\0\0\0\xc6\x01\0\0\
|
||||
\0\0\0\x01\x04\0\0\0\x20\0\0\0\xd3\x01\0\0\0\0\0\x01\x01\0\0\0\x08\0\0\x01\0\0\
|
||||
\0\0\0\0\0\x03\0\0\0\0\x10\0\0\0\x12\0\0\0\x10\0\0\0\xd8\x01\0\0\0\0\0\x01\x04\
|
||||
\0\0\0\x20\0\0\0\0\0\0\0\0\0\0\x02\x14\0\0\0\x3c\x02\0\0\x02\0\0\x04\x10\0\0\0\
|
||||
\x13\0\0\0\x03\0\0\0\0\0\0\0\x4f\x02\0\0\x15\0\0\0\x40\0\0\0\0\0\0\0\0\0\0\x02\
|
||||
\x18\0\0\0\0\0\0\0\x01\0\0\x0d\x06\0\0\0\x1c\0\0\0\x13\0\0\0\x54\x02\0\0\x01\0\
|
||||
\0\x0c\x16\0\0\0\xa0\x02\0\0\x01\0\0\x04\x08\0\0\0\xa9\x02\0\0\x19\0\0\0\0\0\0\
|
||||
\0\0\0\0\0\0\0\0\x02\x1a\0\0\0\xfa\x02\0\0\x06\0\0\x04\x38\0\0\0\xac\x01\0\0\
|
||||
\x0e\0\0\0\0\0\0\0\xaf\x01\0\0\x11\0\0\0\x20\0\0\0\x07\x03\0\0\x1b\0\0\0\xc0\0\
|
||||
\0\0\x18\x03\0\0\x15\0\0\0\0\x01\0\0\x21\x03\0\0\x1d\0\0\0\x40\x01\0\0\x2b\x03\
|
||||
\0\0\x1e\0\0\0\x80\x01\0\0\0\0\0\0\0\0\0\x02\x1c\0\0\0\0\0\0\0\0\0\0\x0a\x10\0\
|
||||
\0\0\0\0\0\0\0\0\0\x02\x1f\0\0\0\0\0\0\0\0\0\0\x02\x20\0\0\0\x75\x03\0\0\x02\0\
|
||||
\0\x04\x08\0\0\0\x83\x03\0\0\x0e\0\0\0\0\0\0\0\x8c\x03\0\0\x0e\0\0\0\x20\0\0\0\
|
||||
\x2b\x03\0\0\x03\0\0\x04\x18\0\0\0\x96\x03\0\0\x1b\0\0\0\0\0\0\0\x9e\x03\0\0\
|
||||
\x21\0\0\0\x40\0\0\0\xa4\x03\0\0\x23\0\0\0\x80\0\0\0\0\0\0\0\0\0\0\x02\x22\0\0\
|
||||
\0\0\0\0\0\0\0\0\x02\x24\0\0\0\xa8\x03\0\0\x01\0\0\x04\x04\0\0\0\xb3\x03\0\0\
|
||||
\x0e\0\0\0\0\0\0\0\x1c\x04\0\0\x01\0\0\x04\x04\0\0\0\x25\x04\0\0\x0e\0\0\0\0\0\
|
||||
\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\x1c\0\0\0\x12\0\0\0\x23\0\0\0\x9b\x04\0\0\0\0\0\
|
||||
\x0e\x25\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\x1c\0\0\0\x12\0\0\0\x0e\0\0\0\
|
||||
\xaf\x04\0\0\0\0\0\x0e\x27\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x03\0\0\0\0\x1c\0\0\0\
|
||||
\x12\0\0\0\x20\0\0\0\xc5\x04\0\0\0\0\0\x0e\x29\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x03\
|
||||
\0\0\0\0\x1c\0\0\0\x12\0\0\0\x11\0\0\0\xda\x04\0\0\0\0\0\x0e\x2b\0\0\0\0\0\0\0\
|
||||
\0\0\0\0\0\0\0\x03\0\0\0\0\x10\0\0\0\x12\0\0\0\x04\0\0\0\xf1\x04\0\0\0\0\0\x0e\
|
||||
\x2d\0\0\0\x01\0\0\0\xf9\x04\0\0\x04\0\0\x0f\0\0\0\0\x26\0\0\0\0\0\0\0\x23\0\0\
|
||||
\0\x28\0\0\0\x23\0\0\0\x0e\0\0\0\x2a\0\0\0\x31\0\0\0\x20\0\0\0\x2c\0\0\0\x51\0\
|
||||
\0\0\x11\0\0\0\x01\x05\0\0\x01\0\0\x0f\0\0\0\0\x2e\0\0\0\0\0\0\0\x04\0\0\0\0\
|
||||
\x62\x70\x66\x5f\x69\x74\x65\x72\x5f\x5f\x62\x70\x66\x5f\x6d\x61\x70\0\x6d\x65\
|
||||
\x74\x61\0\x6d\x61\x70\0\x63\x74\x78\0\x69\x6e\x74\0\x64\x75\x6d\x70\x5f\x62\
|
||||
\x70\x66\x5f\x6d\x61\x70\0\x69\x74\x65\x72\x2f\x62\x70\x66\x5f\x6d\x61\x70\0\
|
||||
\x30\x3a\x30\0\x2f\x68\x6f\x6d\x65\x2f\x61\x6c\x72\x75\x61\x2f\x62\x75\x69\x6c\
|
||||
\x64\x2f\x6c\x69\x6e\x75\x78\x2f\x6b\x65\x72\x6e\x65\x6c\x2f\x62\x70\x66\x2f\
|
||||
\x70\x72\x65\x6c\x6f\x61\x64\x2f\x69\x74\x65\x72\x61\x74\x6f\x72\x73\x2f\x69\
|
||||
\x74\x65\x72\x61\x74\x6f\x72\x73\x2e\x62\x70\x66\x2e\x63\0\x09\x73\x74\x72\x75\
|
||||
\x63\x74\x20\x73\x65\x71\x5f\x66\x69\x6c\x65\x20\x2a\x73\x65\x71\x20\x3d\x20\
|
||||
\x63\x74\x78\x2d\x3e\x6d\x65\x74\x61\x2d\x3e\x73\x65\x71\x3b\0\x62\x70\x66\x5f\
|
||||
\x69\x74\x65\x72\x5f\x6d\x65\x74\x61\0\x73\x65\x71\0\x73\x65\x73\x73\x69\x6f\
|
||||
\x6e\x5f\x69\x64\0\x73\x65\x71\x5f\x6e\x75\x6d\0\x73\x65\x71\x5f\x66\x69\x6c\
|
||||
\x65\0\x5f\x5f\x75\x36\x34\0\x6c\x6f\x6e\x67\x20\x6c\x6f\x6e\x67\x20\x75\x6e\
|
||||
\x73\x69\x67\x6e\x65\x64\x20\x69\x6e\x74\0\x30\x3a\x31\0\x09\x73\x74\x72\x75\
|
||||
\x63\x74\x20\x62\x70\x66\x5f\x6d\x61\x70\x20\x2a\x6d\x61\x70\x20\x3d\x20\x63\
|
||||
\x74\x78\x2d\x3e\x6d\x61\x70\x3b\0\x09\x69\x66\x20\x28\x21\x6d\x61\x70\x29\0\
|
||||
\x30\x3a\x32\0\x09\x5f\x5f\x75\x36\x34\x20\x73\x65\x71\x5f\x6e\x75\x6d\x20\x3d\
|
||||
\x20\x63\x74\x78\x2d\x3e\x6d\x65\x74\x61\x2d\x3e\x73\x65\x71\x5f\x6e\x75\x6d\
|
||||
\x3b\0\x09\x69\x66\x20\x28\x73\x65\x71\x5f\x6e\x75\x6d\x20\x3d\x3d\x20\x30\x29\
|
||||
\0\x09\x09\x42\x50\x46\x5f\x53\x45\x51\x5f\x50\x52\x49\x4e\x54\x46\x28\x73\x65\
|
||||
\x71\x2c\x20\x22\x20\x20\x69\x64\x20\x6e\x61\x6d\x65\x20\x20\x20\x20\x20\x20\
|
||||
\x20\x20\x20\x20\x20\x20\x20\x6d\x61\x78\x5f\x65\x6e\x74\x72\x69\x65\x73\x5c\
|
||||
\x6e\x22\x29\x3b\0\x62\x70\x66\x5f\x6d\x61\x70\0\x69\x64\0\x6e\x61\x6d\x65\0\
|
||||
\x6d\x61\x78\x5f\x65\x6e\x74\x72\x69\x65\x73\0\x5f\x5f\x75\x33\x32\0\x75\x6e\
|
||||
\x73\x69\x67\x6e\x65\x64\x20\x69\x6e\x74\0\x63\x68\x61\x72\0\x5f\x5f\x41\x52\
|
||||
\x52\x41\x59\x5f\x53\x49\x5a\x45\x5f\x54\x59\x50\x45\x5f\x5f\0\x09\x42\x50\x46\
|
||||
\x5f\x53\x45\x51\x5f\x50\x52\x49\x4e\x54\x46\x28\x73\x65\x71\x2c\x20\x22\x25\
|
||||
\x34\x75\x20\x25\x2d\x31\x36\x73\x25\x36\x64\x5c\x6e\x22\x2c\x20\x6d\x61\x70\
|
||||
\x2d\x3e\x69\x64\x2c\x20\x6d\x61\x70\x2d\x3e\x6e\x61\x6d\x65\x2c\x20\x6d\x61\
|
||||
\x70\x2d\x3e\x6d\x61\x78\x5f\x65\x6e\x74\x72\x69\x65\x73\x29\x3b\0\x7d\0\x62\
|
||||
\x70\x66\x5f\x69\x74\x65\x72\x5f\x5f\x62\x70\x66\x5f\x70\x72\x6f\x67\0\x70\x72\
|
||||
\x6f\x67\0\x64\x75\x6d\x70\x5f\x62\x70\x66\x5f\x70\x72\x6f\x67\0\x69\x74\x65\
|
||||
\x72\x2f\x62\x70\x66\x5f\x70\x72\x6f\x67\0\x09\x73\x74\x72\x75\x63\x74\x20\x62\
|
||||
\x70\x66\x5f\x70\x72\x6f\x67\x20\x2a\x70\x72\x6f\x67\x20\x3d\x20\x63\x74\x78\
|
||||
\x2d\x3e\x70\x72\x6f\x67\x3b\0\x09\x69\x66\x20\x28\x21\x70\x72\x6f\x67\x29\0\
|
||||
\x62\x70\x66\x5f\x70\x72\x6f\x67\0\x61\x75\x78\0\x09\x61\x75\x78\x20\x3d\x20\
|
||||
\x70\x72\x6f\x67\x2d\x3e\x61\x75\x78\x3b\0\x09\x09\x42\x50\x46\x5f\x53\x45\x51\
|
||||
\x5f\x50\x52\x49\x4e\x54\x46\x28\x73\x65\x71\x2c\x20\x22\x20\x20\x69\x64\x20\
|
||||
\x6e\x61\x6d\x65\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x61\x74\
|
||||
\x74\x61\x63\x68\x65\x64\x5c\x6e\x22\x29\x3b\0\x62\x70\x66\x5f\x70\x72\x6f\x67\
|
||||
\x5f\x61\x75\x78\0\x61\x74\x74\x61\x63\x68\x5f\x66\x75\x6e\x63\x5f\x6e\x61\x6d\
|
||||
\x65\0\x64\x73\x74\x5f\x70\x72\x6f\x67\0\x66\x75\x6e\x63\x5f\x69\x6e\x66\x6f\0\
|
||||
\x62\x74\x66\0\x09\x42\x50\x46\x5f\x53\x45\x51\x5f\x50\x52\x49\x4e\x54\x46\x28\
|
||||
\x73\x65\x71\x2c\x20\x22\x25\x34\x75\x20\x25\x2d\x31\x36\x73\x20\x25\x73\x20\
|
||||
\x25\x73\x5c\x6e\x22\x2c\x20\x61\x75\x78\x2d\x3e\x69\x64\x2c\0\x30\x3a\x34\0\
|
||||
\x30\x3a\x35\0\x09\x69\x66\x20\x28\x21\x62\x74\x66\x29\0\x62\x70\x66\x5f\x66\
|
||||
\x75\x6e\x63\x5f\x69\x6e\x66\x6f\0\x69\x6e\x73\x6e\x5f\x6f\x66\x66\0\x74\x79\
|
||||
\x70\x65\x5f\x69\x64\0\x30\0\x73\x74\x72\x69\x6e\x67\x73\0\x74\x79\x70\x65\x73\
|
||||
\0\x68\x64\x72\0\x62\x74\x66\x5f\x68\x65\x61\x64\x65\x72\0\x73\x74\x72\x5f\x6c\
|
||||
\x65\x6e\0\x09\x74\x79\x70\x65\x73\x20\x3d\x20\x62\x74\x66\x2d\x3e\x74\x79\x70\
|
||||
\x65\x73\x3b\0\x09\x62\x70\x66\x5f\x70\x72\x6f\x62\x65\x5f\x72\x65\x61\x64\x5f\
|
||||
\x6b\x65\x72\x6e\x65\x6c\x28\x26\x74\x2c\x20\x73\x69\x7a\x65\x6f\x66\x28\x74\
|
||||
\x29\x2c\x20\x74\x79\x70\x65\x73\x20\x2b\x20\x62\x74\x66\x5f\x69\x64\x29\x3b\0\
|
||||
\x09\x73\x74\x72\x20\x3d\x20\x62\x74\x66\x2d\x3e\x73\x74\x72\x69\x6e\x67\x73\
|
||||
\x3b\0\x62\x74\x66\x5f\x74\x79\x70\x65\0\x6e\x61\x6d\x65\x5f\x6f\x66\x66\0\x09\
|
||||
\x6e\x61\x6d\x65\x5f\x6f\x66\x66\x20\x3d\x20\x42\x50\x46\x5f\x43\x4f\x52\x45\
|
||||
\x5f\x52\x45\x41\x44\x28\x74\x2c\x20\x6e\x61\x6d\x65\x5f\x6f\x66\x66\x29\x3b\0\
|
||||
\x30\x3a\x32\x3a\x30\0\x09\x69\x66\x20\x28\x6e\x61\x6d\x65\x5f\x6f\x66\x66\x20\
|
||||
\x3e\x3d\x20\x62\x74\x66\x2d\x3e\x68\x64\x72\x2e\x73\x74\x72\x5f\x6c\x65\x6e\
|
||||
\x29\0\x09\x72\x65\x74\x75\x72\x6e\x20\x73\x74\x72\x20\x2b\x20\x6e\x61\x6d\x65\
|
||||
\x5f\x6f\x66\x66\x3b\0\x30\x3a\x33\0\x64\x75\x6d\x70\x5f\x62\x70\x66\x5f\x6d\
|
||||
\x61\x70\x2e\x5f\x5f\x5f\x66\x6d\x74\0\x64\x75\x6d\x70\x5f\x62\x70\x66\x5f\x6d\
|
||||
\x61\x70\x2e\x5f\x5f\x5f\x66\x6d\x74\x2e\x31\0\x64\x75\x6d\x70\x5f\x62\x70\x66\
|
||||
\x5f\x70\x72\x6f\x67\x2e\x5f\x5f\x5f\x66\x6d\x74\0\x64\x75\x6d\x70\x5f\x62\x70\
|
||||
\x66\x5f\x70\x72\x6f\x67\x2e\x5f\x5f\x5f\x66\x6d\x74\x2e\x32\0\x4c\x49\x43\x45\
|
||||
\x4e\x53\x45\0\x2e\x72\x6f\x64\x61\x74\x61\0\x6c\x69\x63\x65\x6e\x73\x65\0\x9f\
|
||||
\xeb\x01\0\x20\0\0\0\0\0\0\0\x24\0\0\0\x24\0\0\0\x44\x02\0\0\x68\x02\0\0\xa4\
|
||||
\x01\0\0\x08\0\0\0\x31\0\0\0\x01\0\0\0\0\0\0\0\x07\0\0\0\x62\x02\0\0\x01\0\0\0\
|
||||
\0\0\0\0\x17\0\0\0\x10\0\0\0\x31\0\0\0\x09\0\0\0\0\0\0\0\x42\0\0\0\x87\0\0\0\
|
||||
\x1e\x40\x01\0\x08\0\0\0\x42\0\0\0\x87\0\0\0\x24\x40\x01\0\x10\0\0\0\x42\0\0\0\
|
||||
\xfe\0\0\0\x1d\x48\x01\0\x18\0\0\0\x42\0\0\0\x1f\x01\0\0\x06\x50\x01\0\x20\0\0\
|
||||
\0\x42\0\0\0\x2e\x01\0\0\x1d\x44\x01\0\x28\0\0\0\x42\0\0\0\x53\x01\0\0\x06\x5c\
|
||||
\x01\0\x38\0\0\0\x42\0\0\0\x66\x01\0\0\x03\x60\x01\0\x70\0\0\0\x42\0\0\0\xec\
|
||||
\x01\0\0\x02\x68\x01\0\xf0\0\0\0\x42\0\0\0\x3a\x02\0\0\x01\x70\x01\0\x62\x02\0\
|
||||
\0\x1a\0\0\0\0\0\0\0\x42\0\0\0\x87\0\0\0\x1e\x84\x01\0\x08\0\0\0\x42\0\0\0\x87\
|
||||
\0\0\0\x24\x84\x01\0\x10\0\0\0\x42\0\0\0\x70\x02\0\0\x1f\x8c\x01\0\x18\0\0\0\
|
||||
\x42\0\0\0\x94\x02\0\0\x06\x98\x01\0\x20\0\0\0\x42\0\0\0\xad\x02\0\0\x0e\xa4\
|
||||
\x01\0\x28\0\0\0\x42\0\0\0\x2e\x01\0\0\x1d\x88\x01\0\x30\0\0\0\x42\0\0\0\x53\
|
||||
\x01\0\0\x06\xa8\x01\0\x40\0\0\0\x42\0\0\0\xbf\x02\0\0\x03\xac\x01\0\x80\0\0\0\
|
||||
\x42\0\0\0\x2f\x03\0\0\x02\xb4\x01\0\xb8\0\0\0\x42\0\0\0\x6a\x03\0\0\x06\x08\
|
||||
\x01\0\xd0\0\0\0\x42\0\0\0\0\0\0\0\0\0\0\0\xd8\0\0\0\x42\0\0\0\xbb\x03\0\0\x0f\
|
||||
\x14\x01\0\xe0\0\0\0\x42\0\0\0\xd0\x03\0\0\x2d\x18\x01\0\xf0\0\0\0\x42\0\0\0\
|
||||
\x07\x04\0\0\x0d\x10\x01\0\0\x01\0\0\x42\0\0\0\0\0\0\0\0\0\0\0\x08\x01\0\0\x42\
|
||||
\0\0\0\xd0\x03\0\0\x02\x18\x01\0\x20\x01\0\0\x42\0\0\0\x2e\x04\0\0\x0d\x1c\x01\
|
||||
\0\x38\x01\0\0\x42\0\0\0\0\0\0\0\0\0\0\0\x40\x01\0\0\x42\0\0\0\x2e\x04\0\0\x0d\
|
||||
\x1c\x01\0\x58\x01\0\0\x42\0\0\0\x2e\x04\0\0\x0d\x1c\x01\0\x60\x01\0\0\x42\0\0\
|
||||
\0\x5c\x04\0\0\x1b\x20\x01\0\x68\x01\0\0\x42\0\0\0\x5c\x04\0\0\x06\x20\x01\0\
|
||||
\x70\x01\0\0\x42\0\0\0\x7f\x04\0\0\x0d\x28\x01\0\x78\x01\0\0\x42\0\0\0\0\0\0\0\
|
||||
\0\0\0\0\x80\x01\0\0\x42\0\0\0\x2f\x03\0\0\x02\xb4\x01\0\xf8\x01\0\0\x42\0\0\0\
|
||||
\x3a\x02\0\0\x01\xc4\x01\0\x10\0\0\0\x31\0\0\0\x07\0\0\0\0\0\0\0\x02\0\0\0\x3e\
|
||||
\0\0\0\0\0\0\0\x08\0\0\0\x08\0\0\0\x3e\0\0\0\0\0\0\0\x10\0\0\0\x02\0\0\0\xfa\0\
|
||||
\0\0\0\0\0\0\x20\0\0\0\x08\0\0\0\x2a\x01\0\0\0\0\0\0\x70\0\0\0\x0d\0\0\0\x3e\0\
|
||||
\0\0\0\0\0\0\x80\0\0\0\x0d\0\0\0\xfa\0\0\0\0\0\0\0\xa0\0\0\0\x0d\0\0\0\x2a\x01\
|
||||
\0\0\0\0\0\0\x62\x02\0\0\x12\0\0\0\0\0\0\0\x14\0\0\0\x3e\0\0\0\0\0\0\0\x08\0\0\
|
||||
\0\x08\0\0\0\x3e\0\0\0\0\0\0\0\x10\0\0\0\x14\0\0\0\xfa\0\0\0\0\0\0\0\x20\0\0\0\
|
||||
\x18\0\0\0\x3e\0\0\0\0\0\0\0\x28\0\0\0\x08\0\0\0\x2a\x01\0\0\0\0\0\0\x80\0\0\0\
|
||||
\x1a\0\0\0\x3e\0\0\0\0\0\0\0\x90\0\0\0\x1a\0\0\0\xfa\0\0\0\0\0\0\0\xa8\0\0\0\
|
||||
\x1a\0\0\0\x62\x03\0\0\0\0\0\0\xb0\0\0\0\x1a\0\0\0\x66\x03\0\0\0\0\0\0\xc0\0\0\
|
||||
\0\x1f\0\0\0\x94\x03\0\0\0\0\0\0\xd8\0\0\0\x20\0\0\0\xfa\0\0\0\0\0\0\0\xf0\0\0\
|
||||
\0\x20\0\0\0\x3e\0\0\0\0\0\0\0\x18\x01\0\0\x24\0\0\0\x3e\0\0\0\0\0\0\0\x50\x01\
|
||||
\0\0\x1a\0\0\0\xfa\0\0\0\0\0\0\0\x60\x01\0\0\x20\0\0\0\x56\x04\0\0\0\0\0\0\x88\
|
||||
\x01\0\0\x1a\0\0\0\x2a\x01\0\0\0\0\0\0\x98\x01\0\0\x1a\0\0\0\x97\x04\0\0\0\0\0\
|
||||
\0\xa0\x01\0\0\x18\0\0\0\x3e\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
|
||||
\0\0\0\0\0\0\0\x91\0\0\0\x04\0\xf1\xff\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xe6\0\0\
|
||||
\0\0\0\x02\0\x70\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xd8\0\0\0\0\0\x02\0\xf0\0\0\0\0\
|
||||
\0\0\0\0\0\0\0\0\0\0\0\xdf\0\0\0\0\0\x03\0\x78\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
|
||||
\xd1\0\0\0\0\0\x03\0\x80\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xca\0\0\0\0\0\x03\0\
|
||||
\xf8\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x14\0\0\0\x01\0\x04\0\0\0\0\0\0\0\0\0\x23\
|
||||
\0\0\0\0\0\0\0\x04\x01\0\0\x01\0\x04\0\x23\0\0\0\0\0\0\0\x0e\0\0\0\0\0\0\0\x28\
|
||||
\0\0\0\x01\0\x04\0\x31\0\0\0\0\0\0\0\x20\0\0\0\0\0\0\0\xed\0\0\0\x01\0\x04\0\
|
||||
\x51\0\0\0\0\0\0\0\x11\0\0\0\0\0\0\0\0\0\0\0\x03\0\x02\0\0\0\0\0\0\0\0\0\0\0\0\
|
||||
\0\0\0\0\0\0\0\0\0\x03\0\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x03\0\
|
||||
\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xc2\0\0\0\x11\0\x05\0\0\0\0\0\0\0\0\0\
|
||||
\x04\0\0\0\0\0\0\0\x3d\0\0\0\x12\0\x02\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\x5b\
|
||||
\0\0\0\x12\0\x03\0\0\0\0\0\0\0\0\0\x08\x02\0\0\0\0\0\0\x48\0\0\0\0\0\0\0\x01\0\
|
||||
\0\0\x0d\0\0\0\xc8\0\0\0\0\0\0\0\x01\0\0\0\x0d\0\0\0\x50\0\0\0\0\0\0\0\x01\0\0\
|
||||
\0\x0d\0\0\0\xd0\x01\0\0\0\0\0\0\x01\0\0\0\x0d\0\0\0\xf0\x03\0\0\0\0\0\0\x0a\0\
|
||||
\0\0\x0d\0\0\0\xfc\x03\0\0\0\0\0\0\x0a\0\0\0\x0d\0\0\0\x08\x04\0\0\0\0\0\0\x0a\
|
||||
\0\0\0\x0d\0\0\0\x14\x04\0\0\0\0\0\0\x0a\0\0\0\x0d\0\0\0\x2c\x04\0\0\0\0\0\0\0\
|
||||
\0\0\0\x0e\0\0\0\x2c\0\0\0\0\0\0\0\0\0\0\0\x0b\0\0\0\x3c\0\0\0\0\0\0\0\0\0\0\0\
|
||||
\x0c\0\0\0\x50\0\0\0\0\0\0\0\0\0\0\0\x0b\0\0\0\x60\0\0\0\0\0\0\0\0\0\0\0\x0b\0\
|
||||
\0\0\x70\0\0\0\0\0\0\0\0\0\0\0\x0b\0\0\0\x80\0\0\0\0\0\0\0\0\0\0\0\x0b\0\0\0\
|
||||
\x90\0\0\0\0\0\0\0\0\0\0\0\x0b\0\0\0\xa0\0\0\0\0\0\0\0\0\0\0\0\x0b\0\0\0\xb0\0\
|
||||
\0\0\0\0\0\0\0\0\0\0\x0b\0\0\0\xc0\0\0\0\0\0\0\0\0\0\0\0\x0b\0\0\0\xd0\0\0\0\0\
|
||||
\0\0\0\0\0\0\0\x0b\0\0\0\xe8\0\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\xf8\0\0\0\0\0\0\0\
|
||||
\0\0\0\0\x0c\0\0\0\x08\x01\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\x18\x01\0\0\0\0\0\0\0\
|
||||
\0\0\0\x0c\0\0\0\x28\x01\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\x38\x01\0\0\0\0\0\0\0\0\
|
||||
\0\0\x0c\0\0\0\x48\x01\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\x58\x01\0\0\0\0\0\0\0\0\0\
|
||||
\0\x0c\0\0\0\x68\x01\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\x78\x01\0\0\0\0\0\0\0\0\0\0\
|
||||
\x0c\0\0\0\x88\x01\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\x98\x01\0\0\0\0\0\0\0\0\0\0\
|
||||
\x0c\0\0\0\xa8\x01\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\xb8\x01\0\0\0\0\0\0\0\0\0\0\
|
||||
\x0c\0\0\0\xc8\x01\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\xd8\x01\0\0\0\0\0\0\0\0\0\0\
|
||||
\x0c\0\0\0\xe8\x01\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\xf8\x01\0\0\0\0\0\0\0\0\0\0\
|
||||
\x0c\0\0\0\x08\x02\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\x18\x02\0\0\0\0\0\0\0\0\0\0\
|
||||
\x0c\0\0\0\x28\x02\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\x38\x02\0\0\0\0\0\0\0\0\0\0\
|
||||
\x0c\0\0\0\x48\x02\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\x58\x02\0\0\0\0\0\0\0\0\0\0\
|
||||
\x0c\0\0\0\x68\x02\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\x78\x02\0\0\0\0\0\0\0\0\0\0\
|
||||
\x0c\0\0\0\x94\x02\0\0\0\0\0\0\0\0\0\0\x0b\0\0\0\xa4\x02\0\0\0\0\0\0\0\0\0\0\
|
||||
\x0b\0\0\0\xb4\x02\0\0\0\0\0\0\0\0\0\0\x0b\0\0\0\xc4\x02\0\0\0\0\0\0\0\0\0\0\
|
||||
\x0b\0\0\0\xd4\x02\0\0\0\0\0\0\0\0\0\0\x0b\0\0\0\xe4\x02\0\0\0\0\0\0\0\0\0\0\
|
||||
\x0b\0\0\0\xf4\x02\0\0\0\0\0\0\0\0\0\0\x0b\0\0\0\x0c\x03\0\0\0\0\0\0\0\0\0\0\
|
||||
\x0c\0\0\0\x1c\x03\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\x2c\x03\0\0\0\0\0\0\0\0\0\0\
|
||||
\x0c\0\0\0\x3c\x03\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\x4c\x03\0\0\0\0\0\0\0\0\0\0\
|
||||
\x0c\0\0\0\x5c\x03\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\x6c\x03\0\0\0\0\0\0\0\0\0\0\
|
||||
\x0c\0\0\0\x7c\x03\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\x8c\x03\0\0\0\0\0\0\0\0\0\0\
|
||||
\x0c\0\0\0\x9c\x03\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\xac\x03\0\0\0\0\0\0\0\0\0\0\
|
||||
\x0c\0\0\0\xbc\x03\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\xcc\x03\0\0\0\0\0\0\0\0\0\0\
|
||||
\x0c\0\0\0\xdc\x03\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\xec\x03\0\0\0\0\0\0\0\0\0\0\
|
||||
\x0c\0\0\0\xfc\x03\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\x0c\x04\0\0\0\0\0\0\0\0\0\0\
|
||||
\x0c\0\0\0\x1c\x04\0\0\0\0\0\0\0\0\0\0\x0c\0\0\0\x4d\x4e\x40\x41\x42\x43\x4c\0\
|
||||
\x2e\x74\x65\x78\x74\0\x2e\x72\x65\x6c\x2e\x42\x54\x46\x2e\x65\x78\x74\0\x64\
|
||||
\x75\x6d\x70\x5f\x62\x70\x66\x5f\x6d\x61\x70\x2e\x5f\x5f\x5f\x66\x6d\x74\0\x64\
|
||||
\x75\x6d\x70\x5f\x62\x70\x66\x5f\x70\x72\x6f\x67\x2e\x5f\x5f\x5f\x66\x6d\x74\0\
|
||||
\x64\x75\x6d\x70\x5f\x62\x70\x66\x5f\x6d\x61\x70\0\x2e\x72\x65\x6c\x69\x74\x65\
|
||||
\x72\x2f\x62\x70\x66\x5f\x6d\x61\x70\0\x64\x75\x6d\x70\x5f\x62\x70\x66\x5f\x70\
|
||||
\x72\x6f\x67\0\x2e\x72\x65\x6c\x69\x74\x65\x72\x2f\x62\x70\x66\x5f\x70\x72\x6f\
|
||||
\x67\0\x2e\x6c\x6c\x76\x6d\x5f\x61\x64\x64\x72\x73\x69\x67\0\x6c\x69\x63\x65\
|
||||
\x6e\x73\x65\0\x69\x74\x65\x72\x61\x74\x6f\x72\x73\x2e\x62\x70\x66\x2e\x63\0\
|
||||
\x2e\x73\x74\x72\x74\x61\x62\0\x2e\x73\x79\x6d\x74\x61\x62\0\x2e\x72\x6f\x64\
|
||||
\x61\x74\x61\0\x2e\x72\x65\x6c\x2e\x42\x54\x46\0\x4c\x49\x43\x45\x4e\x53\x45\0\
|
||||
\x4c\x42\x42\x31\x5f\x37\0\x4c\x42\x42\x31\x5f\x36\0\x4c\x42\x42\x30\x5f\x34\0\
|
||||
\x4c\x42\x42\x31\x5f\x33\0\x4c\x42\x42\x30\x5f\x33\0\x64\x75\x6d\x70\x5f\x62\
|
||||
\x70\x66\x5f\x70\x72\x6f\x67\x2e\x5f\x5f\x5f\x66\x6d\x74\x2e\x32\0\x64\x75\x6d\
|
||||
\x70\x5f\x62\x70\x66\x5f\x6d\x61\x70\x2e\x5f\x5f\x5f\x66\x6d\x74\x2e\x31\0\0\0\
|
||||
\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
|
||||
\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\x01\0\0\
|
||||
\0\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x40\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
|
||||
\0\0\0\0\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x4e\0\0\0\x01\0\0\0\x06\0\0\0\0\0\0\
|
||||
\0\0\0\0\0\0\0\0\0\x40\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x08\0\0\
|
||||
\0\0\0\0\0\0\0\0\0\0\0\0\0\x6d\0\0\0\x01\0\0\0\x06\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
|
||||
\0\x40\x01\0\0\0\0\0\0\x08\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x08\0\0\0\0\0\0\0\0\
|
||||
\0\0\0\0\0\0\0\xb1\0\0\0\x01\0\0\0\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x48\x03\0\
|
||||
\0\0\0\0\0\x62\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
|
||||
\x89\0\0\0\x01\0\0\0\x03\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xaa\x03\0\0\0\0\0\0\x04\
|
||||
\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xbd\0\0\0\x01\
|
||||
\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xae\x03\0\0\0\0\0\0\x3d\x09\0\0\0\0\0\0\
|
||||
\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x0b\0\0\0\x01\0\0\0\0\0\0\0\
|
||||
\0\0\0\0\0\0\0\0\0\0\0\0\xeb\x0c\0\0\0\0\0\0\x2c\x04\0\0\0\0\0\0\0\0\0\0\0\0\0\
|
||||
\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xa9\0\0\0\x02\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
|
||||
\0\0\0\0\0\x18\x11\0\0\0\0\0\0\x98\x01\0\0\0\0\0\0\x0e\0\0\0\x0e\0\0\0\x08\0\0\
|
||||
\0\0\0\0\0\x18\0\0\0\0\0\0\0\x4a\0\0\0\x09\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\
|
||||
\0\xb0\x12\0\0\0\0\0\0\x20\0\0\0\0\0\0\0\x08\0\0\0\x02\0\0\0\x08\0\0\0\0\0\0\0\
|
||||
\x10\0\0\0\0\0\0\0\x69\0\0\0\x09\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xd0\x12\
|
||||
\0\0\0\0\0\0\x20\0\0\0\0\0\0\0\x08\0\0\0\x03\0\0\0\x08\0\0\0\0\0\0\0\x10\0\0\0\
|
||||
\0\0\0\0\xb9\0\0\0\x09\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xf0\x12\0\0\0\0\0\
|
||||
\0\x50\0\0\0\0\0\0\0\x08\0\0\0\x06\0\0\0\x08\0\0\0\0\0\0\0\x10\0\0\0\0\0\0\0\
|
||||
\x07\0\0\0\x09\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x40\x13\0\0\0\0\0\0\xe0\
|
||||
\x03\0\0\0\0\0\0\x08\0\0\0\x07\0\0\0\x08\0\0\0\0\0\0\0\x10\0\0\0\0\0\0\0\x7b\0\
|
||||
\0\0\x03\x4c\xff\x6f\0\0\0\x80\0\0\0\0\0\0\0\0\0\0\0\0\x20\x17\0\0\0\0\0\0\x07\
|
||||
\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\xa1\0\0\0\x03\
|
||||
\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x27\x17\0\0\0\0\0\0\x1a\x01\0\0\0\0\0\0\
|
||||
\0\0\0\0\0\0\0\0\x01\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
|
||||
|
||||
return 0;
|
||||
err:
|
||||
bpf_object__destroy_skeleton(s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif /* __ITERATORS_BPF_SKEL_H__ */
|
||||
@@ -257,6 +257,7 @@ static int queue_stack_map_get_next_key(struct bpf_map *map, void *key,
|
||||
|
||||
static int queue_map_btf_id;
|
||||
const struct bpf_map_ops queue_map_ops = {
|
||||
.map_meta_equal = bpf_map_meta_equal,
|
||||
.map_alloc_check = queue_stack_map_alloc_check,
|
||||
.map_alloc = queue_stack_map_alloc,
|
||||
.map_free = queue_stack_map_free,
|
||||
@@ -273,6 +274,7 @@ const struct bpf_map_ops queue_map_ops = {
|
||||
|
||||
static int stack_map_btf_id;
|
||||
const struct bpf_map_ops stack_map_ops = {
|
||||
.map_meta_equal = bpf_map_meta_equal,
|
||||
.map_alloc_check = queue_stack_map_alloc_check,
|
||||
.map_alloc = queue_stack_map_alloc,
|
||||
.map_free = queue_stack_map_free,
|
||||
|
||||
@@ -191,7 +191,7 @@ int bpf_fd_reuseport_array_lookup_elem(struct bpf_map *map, void *key,
|
||||
rcu_read_lock();
|
||||
sk = reuseport_array_lookup_elem(map, key);
|
||||
if (sk) {
|
||||
*(u64 *)value = sock_gen_cookie(sk);
|
||||
*(u64 *)value = __sock_gen_cookie(sk);
|
||||
err = 0;
|
||||
} else {
|
||||
err = -ENOENT;
|
||||
@@ -351,6 +351,7 @@ static int reuseport_array_get_next_key(struct bpf_map *map, void *key,
|
||||
|
||||
static int reuseport_array_map_btf_id;
|
||||
const struct bpf_map_ops reuseport_array_ops = {
|
||||
.map_meta_equal = bpf_map_meta_equal,
|
||||
.map_alloc_check = reuseport_array_alloc_check,
|
||||
.map_alloc = reuseport_array_alloc,
|
||||
.map_free = reuseport_array_free,
|
||||
|
||||
@@ -287,6 +287,7 @@ static __poll_t ringbuf_map_poll(struct bpf_map *map, struct file *filp,
|
||||
|
||||
static int ringbuf_map_btf_id;
|
||||
const struct bpf_map_ops ringbuf_map_ops = {
|
||||
.map_meta_equal = bpf_map_meta_equal,
|
||||
.map_alloc = ringbuf_map_alloc,
|
||||
.map_free = ringbuf_map_free,
|
||||
.map_mmap = ringbuf_map_mmap,
|
||||
|
||||
+17
-13
@@ -213,11 +213,13 @@ static int stack_map_get_build_id_32(void *page_addr,
|
||||
|
||||
phdr = (Elf32_Phdr *)(page_addr + sizeof(Elf32_Ehdr));
|
||||
|
||||
for (i = 0; i < ehdr->e_phnum; ++i)
|
||||
if (phdr[i].p_type == PT_NOTE)
|
||||
return stack_map_parse_build_id(page_addr, build_id,
|
||||
page_addr + phdr[i].p_offset,
|
||||
phdr[i].p_filesz);
|
||||
for (i = 0; i < ehdr->e_phnum; ++i) {
|
||||
if (phdr[i].p_type == PT_NOTE &&
|
||||
!stack_map_parse_build_id(page_addr, build_id,
|
||||
page_addr + phdr[i].p_offset,
|
||||
phdr[i].p_filesz))
|
||||
return 0;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -236,11 +238,13 @@ static int stack_map_get_build_id_64(void *page_addr,
|
||||
|
||||
phdr = (Elf64_Phdr *)(page_addr + sizeof(Elf64_Ehdr));
|
||||
|
||||
for (i = 0; i < ehdr->e_phnum; ++i)
|
||||
if (phdr[i].p_type == PT_NOTE)
|
||||
return stack_map_parse_build_id(page_addr, build_id,
|
||||
page_addr + phdr[i].p_offset,
|
||||
phdr[i].p_filesz);
|
||||
for (i = 0; i < ehdr->e_phnum; ++i) {
|
||||
if (phdr[i].p_type == PT_NOTE &&
|
||||
!stack_map_parse_build_id(page_addr, build_id,
|
||||
page_addr + phdr[i].p_offset,
|
||||
phdr[i].p_filesz))
|
||||
return 0;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -661,18 +665,17 @@ BPF_CALL_4(bpf_get_task_stack, struct task_struct *, task, void *, buf,
|
||||
return __bpf_get_stack(regs, task, NULL, buf, size, flags);
|
||||
}
|
||||
|
||||
BTF_ID_LIST(bpf_get_task_stack_btf_ids)
|
||||
BTF_ID(struct, task_struct)
|
||||
BTF_ID_LIST_SINGLE(bpf_get_task_stack_btf_ids, struct, task_struct)
|
||||
|
||||
const struct bpf_func_proto bpf_get_task_stack_proto = {
|
||||
.func = bpf_get_task_stack,
|
||||
.gpl_only = false,
|
||||
.ret_type = RET_INTEGER,
|
||||
.arg1_type = ARG_PTR_TO_BTF_ID,
|
||||
.arg1_btf_id = &bpf_get_task_stack_btf_ids[0],
|
||||
.arg2_type = ARG_PTR_TO_UNINIT_MEM,
|
||||
.arg3_type = ARG_CONST_SIZE_OR_ZERO,
|
||||
.arg4_type = ARG_ANYTHING,
|
||||
.btf_id = bpf_get_task_stack_btf_ids,
|
||||
};
|
||||
|
||||
BPF_CALL_4(bpf_get_stack_pe, struct bpf_perf_event_data_kern *, ctx,
|
||||
@@ -835,6 +838,7 @@ static void stack_map_free(struct bpf_map *map)
|
||||
|
||||
static int stack_trace_map_btf_id;
|
||||
const struct bpf_map_ops stack_trace_map_ops = {
|
||||
.map_meta_equal = bpf_map_meta_equal,
|
||||
.map_alloc = stack_map_alloc,
|
||||
.map_free = stack_map_free,
|
||||
.map_get_next_key = stack_map_get_next_key,
|
||||
|
||||
+280
-56
@@ -4,6 +4,7 @@
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/bpf_trace.h>
|
||||
#include <linux/bpf_lirc.h>
|
||||
#include <linux/bpf_verifier.h>
|
||||
#include <linux/btf.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/slab.h>
|
||||
@@ -29,6 +30,7 @@
|
||||
#include <linux/bpf_lsm.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/bpf-netns.h>
|
||||
#include <linux/rcupdate_trace.h>
|
||||
|
||||
#define IS_FD_ARRAY(map) ((map)->map_type == BPF_MAP_TYPE_PERF_EVENT_ARRAY || \
|
||||
(map)->map_type == BPF_MAP_TYPE_CGROUP_ARRAY || \
|
||||
@@ -90,6 +92,7 @@ int bpf_check_uarg_tail_zero(void __user *uaddr,
|
||||
}
|
||||
|
||||
const struct bpf_map_ops bpf_map_offload_ops = {
|
||||
.map_meta_equal = bpf_map_meta_equal,
|
||||
.map_alloc = bpf_map_offload_map_alloc,
|
||||
.map_free = bpf_map_offload_map_free,
|
||||
.map_check_btf = map_check_no_btf,
|
||||
@@ -157,10 +160,11 @@ static int bpf_map_update_value(struct bpf_map *map, struct fd f, void *key,
|
||||
if (bpf_map_is_dev_bound(map)) {
|
||||
return bpf_map_offload_update_elem(map, key, value, flags);
|
||||
} else if (map->map_type == BPF_MAP_TYPE_CPUMAP ||
|
||||
map->map_type == BPF_MAP_TYPE_SOCKHASH ||
|
||||
map->map_type == BPF_MAP_TYPE_SOCKMAP ||
|
||||
map->map_type == BPF_MAP_TYPE_STRUCT_OPS) {
|
||||
return map->ops->map_update_elem(map, key, value, flags);
|
||||
} else if (map->map_type == BPF_MAP_TYPE_SOCKHASH ||
|
||||
map->map_type == BPF_MAP_TYPE_SOCKMAP) {
|
||||
return sock_map_update_elem_sys(map, key, value, flags);
|
||||
} else if (IS_FD_PROG_ARRAY(map)) {
|
||||
return bpf_fd_array_map_update_elem(map, f.file, key, value,
|
||||
flags);
|
||||
@@ -768,7 +772,8 @@ static int map_check_btf(struct bpf_map *map, const struct btf *btf,
|
||||
if (map->map_type != BPF_MAP_TYPE_HASH &&
|
||||
map->map_type != BPF_MAP_TYPE_ARRAY &&
|
||||
map->map_type != BPF_MAP_TYPE_CGROUP_STORAGE &&
|
||||
map->map_type != BPF_MAP_TYPE_SK_STORAGE)
|
||||
map->map_type != BPF_MAP_TYPE_SK_STORAGE &&
|
||||
map->map_type != BPF_MAP_TYPE_INODE_STORAGE)
|
||||
return -ENOTSUPP;
|
||||
if (map->spin_lock_off + sizeof(struct bpf_spin_lock) >
|
||||
map->value_size) {
|
||||
@@ -1728,10 +1733,14 @@ static void __bpf_prog_put_noref(struct bpf_prog *prog, bool deferred)
|
||||
btf_put(prog->aux->btf);
|
||||
bpf_prog_free_linfo(prog);
|
||||
|
||||
if (deferred)
|
||||
call_rcu(&prog->aux->rcu, __bpf_prog_put_rcu);
|
||||
else
|
||||
if (deferred) {
|
||||
if (prog->aux->sleepable)
|
||||
call_rcu_tasks_trace(&prog->aux->rcu, __bpf_prog_put_rcu);
|
||||
else
|
||||
call_rcu(&prog->aux->rcu, __bpf_prog_put_rcu);
|
||||
} else {
|
||||
__bpf_prog_put_rcu(&prog->aux->rcu);
|
||||
}
|
||||
}
|
||||
|
||||
static void __bpf_prog_put(struct bpf_prog *prog, bool do_idr_lock)
|
||||
@@ -2029,7 +2038,7 @@ bpf_prog_load_check_attach(enum bpf_prog_type prog_type,
|
||||
case BPF_PROG_TYPE_EXT:
|
||||
if (expected_attach_type)
|
||||
return -EINVAL;
|
||||
/* fallthrough */
|
||||
fallthrough;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
@@ -2101,6 +2110,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_SLEEPABLE |
|
||||
BPF_F_TEST_RND_HI32))
|
||||
return -EINVAL;
|
||||
|
||||
@@ -2145,17 +2155,18 @@ static int bpf_prog_load(union bpf_attr *attr, union bpf_attr __user *uattr)
|
||||
prog->expected_attach_type = attr->expected_attach_type;
|
||||
prog->aux->attach_btf_id = attr->attach_btf_id;
|
||||
if (attr->attach_prog_fd) {
|
||||
struct bpf_prog *tgt_prog;
|
||||
struct bpf_prog *dst_prog;
|
||||
|
||||
tgt_prog = bpf_prog_get(attr->attach_prog_fd);
|
||||
if (IS_ERR(tgt_prog)) {
|
||||
err = PTR_ERR(tgt_prog);
|
||||
dst_prog = bpf_prog_get(attr->attach_prog_fd);
|
||||
if (IS_ERR(dst_prog)) {
|
||||
err = PTR_ERR(dst_prog);
|
||||
goto free_prog_nouncharge;
|
||||
}
|
||||
prog->aux->linked_prog = tgt_prog;
|
||||
prog->aux->dst_prog = dst_prog;
|
||||
}
|
||||
|
||||
prog->aux->offload_requested = !!attr->prog_ifindex;
|
||||
prog->aux->sleepable = attr->prog_flags & BPF_F_SLEEPABLE;
|
||||
|
||||
err = security_bpf_prog_alloc(prog->aux);
|
||||
if (err)
|
||||
@@ -2488,11 +2499,23 @@ struct bpf_link *bpf_link_get_from_fd(u32 ufd)
|
||||
struct bpf_tracing_link {
|
||||
struct bpf_link link;
|
||||
enum bpf_attach_type attach_type;
|
||||
struct bpf_trampoline *trampoline;
|
||||
struct bpf_prog *tgt_prog;
|
||||
};
|
||||
|
||||
static void bpf_tracing_link_release(struct bpf_link *link)
|
||||
{
|
||||
WARN_ON_ONCE(bpf_trampoline_unlink_prog(link->prog));
|
||||
struct bpf_tracing_link *tr_link =
|
||||
container_of(link, struct bpf_tracing_link, link);
|
||||
|
||||
WARN_ON_ONCE(bpf_trampoline_unlink_prog(link->prog,
|
||||
tr_link->trampoline));
|
||||
|
||||
bpf_trampoline_put(tr_link->trampoline);
|
||||
|
||||
/* tgt_prog is NULL if target is a kernel function */
|
||||
if (tr_link->tgt_prog)
|
||||
bpf_prog_put(tr_link->tgt_prog);
|
||||
}
|
||||
|
||||
static void bpf_tracing_link_dealloc(struct bpf_link *link)
|
||||
@@ -2532,10 +2555,15 @@ static const struct bpf_link_ops bpf_tracing_link_lops = {
|
||||
.fill_link_info = bpf_tracing_link_fill_link_info,
|
||||
};
|
||||
|
||||
static int bpf_tracing_prog_attach(struct bpf_prog *prog)
|
||||
static int bpf_tracing_prog_attach(struct bpf_prog *prog,
|
||||
int tgt_prog_fd,
|
||||
u32 btf_id)
|
||||
{
|
||||
struct bpf_link_primer link_primer;
|
||||
struct bpf_prog *tgt_prog = NULL;
|
||||
struct bpf_trampoline *tr = NULL;
|
||||
struct bpf_tracing_link *link;
|
||||
u64 key = 0;
|
||||
int err;
|
||||
|
||||
switch (prog->type) {
|
||||
@@ -2564,6 +2592,28 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog)
|
||||
goto out_put_prog;
|
||||
}
|
||||
|
||||
if (!!tgt_prog_fd != !!btf_id) {
|
||||
err = -EINVAL;
|
||||
goto out_put_prog;
|
||||
}
|
||||
|
||||
if (tgt_prog_fd) {
|
||||
/* For now we only allow new targets for BPF_PROG_TYPE_EXT */
|
||||
if (prog->type != BPF_PROG_TYPE_EXT) {
|
||||
err = -EINVAL;
|
||||
goto out_put_prog;
|
||||
}
|
||||
|
||||
tgt_prog = bpf_prog_get(tgt_prog_fd);
|
||||
if (IS_ERR(tgt_prog)) {
|
||||
err = PTR_ERR(tgt_prog);
|
||||
tgt_prog = NULL;
|
||||
goto out_put_prog;
|
||||
}
|
||||
|
||||
key = bpf_trampoline_compute_key(tgt_prog, btf_id);
|
||||
}
|
||||
|
||||
link = kzalloc(sizeof(*link), GFP_USER);
|
||||
if (!link) {
|
||||
err = -ENOMEM;
|
||||
@@ -2573,20 +2623,100 @@ static int bpf_tracing_prog_attach(struct bpf_prog *prog)
|
||||
&bpf_tracing_link_lops, prog);
|
||||
link->attach_type = prog->expected_attach_type;
|
||||
|
||||
err = bpf_link_prime(&link->link, &link_primer);
|
||||
if (err) {
|
||||
kfree(link);
|
||||
goto out_put_prog;
|
||||
mutex_lock(&prog->aux->dst_mutex);
|
||||
|
||||
/* There are a few possible cases here:
|
||||
*
|
||||
* - if prog->aux->dst_trampoline is set, the program was just loaded
|
||||
* and not yet attached to anything, so we can use the values stored
|
||||
* in prog->aux
|
||||
*
|
||||
* - if prog->aux->dst_trampoline is NULL, the program has already been
|
||||
* attached to a target and its initial target was cleared (below)
|
||||
*
|
||||
* - if tgt_prog != NULL, the caller specified tgt_prog_fd +
|
||||
* target_btf_id using the link_create API.
|
||||
*
|
||||
* - if tgt_prog == NULL when this function was called using the old
|
||||
* raw_tracepoint_open API, and we need a target from prog->aux
|
||||
*
|
||||
* The combination of no saved target in prog->aux, and no target
|
||||
* specified on load is illegal, and we reject that here.
|
||||
*/
|
||||
if (!prog->aux->dst_trampoline && !tgt_prog) {
|
||||
err = -ENOENT;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
err = bpf_trampoline_link_prog(prog);
|
||||
if (!prog->aux->dst_trampoline ||
|
||||
(key && key != prog->aux->dst_trampoline->key)) {
|
||||
/* If there is no saved target, or the specified target is
|
||||
* different from the destination specified at load time, we
|
||||
* need a new trampoline and a check for compatibility
|
||||
*/
|
||||
struct bpf_attach_target_info tgt_info = {};
|
||||
|
||||
err = bpf_check_attach_target(NULL, prog, tgt_prog, btf_id,
|
||||
&tgt_info);
|
||||
if (err)
|
||||
goto out_unlock;
|
||||
|
||||
tr = bpf_trampoline_get(key, &tgt_info);
|
||||
if (!tr) {
|
||||
err = -ENOMEM;
|
||||
goto out_unlock;
|
||||
}
|
||||
} else {
|
||||
/* The caller didn't specify a target, or the target was the
|
||||
* same as the destination supplied during program load. This
|
||||
* means we can reuse the trampoline and reference from program
|
||||
* load time, and there is no need to allocate a new one. This
|
||||
* can only happen once for any program, as the saved values in
|
||||
* prog->aux are cleared below.
|
||||
*/
|
||||
tr = prog->aux->dst_trampoline;
|
||||
tgt_prog = prog->aux->dst_prog;
|
||||
}
|
||||
|
||||
err = bpf_link_prime(&link->link, &link_primer);
|
||||
if (err)
|
||||
goto out_unlock;
|
||||
|
||||
err = bpf_trampoline_link_prog(prog, tr);
|
||||
if (err) {
|
||||
bpf_link_cleanup(&link_primer);
|
||||
goto out_put_prog;
|
||||
link = NULL;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
link->tgt_prog = tgt_prog;
|
||||
link->trampoline = tr;
|
||||
|
||||
/* Always clear the trampoline and target prog from prog->aux to make
|
||||
* sure the original attach destination is not kept alive after a
|
||||
* program is (re-)attached to another target.
|
||||
*/
|
||||
if (prog->aux->dst_prog &&
|
||||
(tgt_prog_fd || tr != prog->aux->dst_trampoline))
|
||||
/* got extra prog ref from syscall, or attaching to different prog */
|
||||
bpf_prog_put(prog->aux->dst_prog);
|
||||
if (prog->aux->dst_trampoline && tr != prog->aux->dst_trampoline)
|
||||
/* we allocated a new trampoline, so free the old one */
|
||||
bpf_trampoline_put(prog->aux->dst_trampoline);
|
||||
|
||||
prog->aux->dst_prog = NULL;
|
||||
prog->aux->dst_trampoline = NULL;
|
||||
mutex_unlock(&prog->aux->dst_mutex);
|
||||
|
||||
return bpf_link_settle(&link_primer);
|
||||
out_unlock:
|
||||
if (tr && tr != prog->aux->dst_trampoline)
|
||||
bpf_trampoline_put(tr);
|
||||
mutex_unlock(&prog->aux->dst_mutex);
|
||||
kfree(link);
|
||||
out_put_prog:
|
||||
if (tgt_prog_fd && tgt_prog)
|
||||
bpf_prog_put(tgt_prog);
|
||||
bpf_prog_put(prog);
|
||||
return err;
|
||||
}
|
||||
@@ -2634,7 +2764,7 @@ static int bpf_raw_tp_link_fill_link_info(const struct bpf_link *link,
|
||||
u32 ulen = info->raw_tracepoint.tp_name_len;
|
||||
size_t tp_len = strlen(tp_name);
|
||||
|
||||
if (ulen && !ubuf)
|
||||
if (!ulen ^ !ubuf)
|
||||
return -EINVAL;
|
||||
|
||||
info->raw_tracepoint.tp_name_len = tp_len + 1;
|
||||
@@ -2700,7 +2830,7 @@ static int bpf_raw_tracepoint_open(const union bpf_attr *attr)
|
||||
tp_name = prog->aux->attach_func_name;
|
||||
break;
|
||||
}
|
||||
return bpf_tracing_prog_attach(prog);
|
||||
return bpf_tracing_prog_attach(prog, 0, 0);
|
||||
case BPF_PROG_TYPE_RAW_TRACEPOINT:
|
||||
case BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE:
|
||||
if (strncpy_from_user(buf,
|
||||
@@ -2783,7 +2913,6 @@ attach_type_to_prog_type(enum bpf_attach_type attach_type)
|
||||
case BPF_CGROUP_INET_INGRESS:
|
||||
case BPF_CGROUP_INET_EGRESS:
|
||||
return BPF_PROG_TYPE_CGROUP_SKB;
|
||||
break;
|
||||
case BPF_CGROUP_INET_SOCK_CREATE:
|
||||
case BPF_CGROUP_INET_SOCK_RELEASE:
|
||||
case BPF_CGROUP_INET4_POST_BIND:
|
||||
@@ -2969,7 +3098,7 @@ static int bpf_prog_query(const union bpf_attr *attr,
|
||||
}
|
||||
}
|
||||
|
||||
#define BPF_PROG_TEST_RUN_LAST_FIELD test.ctx_out
|
||||
#define BPF_PROG_TEST_RUN_LAST_FIELD test.cpu
|
||||
|
||||
static int bpf_prog_test_run(const union bpf_attr *attr,
|
||||
union bpf_attr __user *uattr)
|
||||
@@ -3152,21 +3281,25 @@ static const struct bpf_map *bpf_map_from_imm(const struct bpf_prog *prog,
|
||||
const struct bpf_map *map;
|
||||
int i;
|
||||
|
||||
mutex_lock(&prog->aux->used_maps_mutex);
|
||||
for (i = 0, *off = 0; i < prog->aux->used_map_cnt; i++) {
|
||||
map = prog->aux->used_maps[i];
|
||||
if (map == (void *)addr) {
|
||||
*type = BPF_PSEUDO_MAP_FD;
|
||||
return map;
|
||||
goto out;
|
||||
}
|
||||
if (!map->ops->map_direct_value_meta)
|
||||
continue;
|
||||
if (!map->ops->map_direct_value_meta(map, addr, off)) {
|
||||
*type = BPF_PSEUDO_MAP_VALUE;
|
||||
return map;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
map = NULL;
|
||||
|
||||
return NULL;
|
||||
out:
|
||||
mutex_unlock(&prog->aux->used_maps_mutex);
|
||||
return map;
|
||||
}
|
||||
|
||||
static struct bpf_insn *bpf_insn_prepare_dump(const struct bpf_prog *prog,
|
||||
@@ -3284,6 +3417,7 @@ static int bpf_prog_get_info_by_fd(struct file *file,
|
||||
memcpy(info.tag, prog->tag, sizeof(prog->tag));
|
||||
memcpy(info.name, prog->aux->name, sizeof(prog->aux->name));
|
||||
|
||||
mutex_lock(&prog->aux->used_maps_mutex);
|
||||
ulen = info.nr_map_ids;
|
||||
info.nr_map_ids = prog->aux->used_map_cnt;
|
||||
ulen = min_t(u32, info.nr_map_ids, ulen);
|
||||
@@ -3293,9 +3427,12 @@ static int bpf_prog_get_info_by_fd(struct file *file,
|
||||
|
||||
for (i = 0; i < ulen; i++)
|
||||
if (put_user(prog->aux->used_maps[i]->id,
|
||||
&user_map_ids[i]))
|
||||
&user_map_ids[i])) {
|
||||
mutex_unlock(&prog->aux->used_maps_mutex);
|
||||
return -EFAULT;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&prog->aux->used_maps_mutex);
|
||||
|
||||
err = set_info_rec_size(&info);
|
||||
if (err)
|
||||
@@ -3876,10 +4013,15 @@ err_put:
|
||||
|
||||
static int tracing_bpf_link_attach(const union bpf_attr *attr, struct bpf_prog *prog)
|
||||
{
|
||||
if (attr->link_create.attach_type == BPF_TRACE_ITER &&
|
||||
prog->expected_attach_type == BPF_TRACE_ITER)
|
||||
return bpf_iter_link_attach(attr, prog);
|
||||
if (attr->link_create.attach_type != prog->expected_attach_type)
|
||||
return -EINVAL;
|
||||
|
||||
if (prog->expected_attach_type == BPF_TRACE_ITER)
|
||||
return bpf_iter_link_attach(attr, prog);
|
||||
else if (prog->type == BPF_PROG_TYPE_EXT)
|
||||
return bpf_tracing_prog_attach(prog,
|
||||
attr->link_create.target_fd,
|
||||
attr->link_create.target_btf_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -3893,18 +4035,25 @@ static int link_create(union bpf_attr *attr)
|
||||
if (CHECK_ATTR(BPF_LINK_CREATE))
|
||||
return -EINVAL;
|
||||
|
||||
ptype = attach_type_to_prog_type(attr->link_create.attach_type);
|
||||
if (ptype == BPF_PROG_TYPE_UNSPEC)
|
||||
return -EINVAL;
|
||||
|
||||
prog = bpf_prog_get_type(attr->link_create.prog_fd, ptype);
|
||||
prog = bpf_prog_get(attr->link_create.prog_fd);
|
||||
if (IS_ERR(prog))
|
||||
return PTR_ERR(prog);
|
||||
|
||||
ret = bpf_prog_attach_check_attach_type(prog,
|
||||
attr->link_create.attach_type);
|
||||
if (ret)
|
||||
goto err_out;
|
||||
goto out;
|
||||
|
||||
if (prog->type == BPF_PROG_TYPE_EXT) {
|
||||
ret = tracing_bpf_link_attach(attr, prog);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ptype = attach_type_to_prog_type(attr->link_create.attach_type);
|
||||
if (ptype == BPF_PROG_TYPE_UNSPEC || ptype != prog->type) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (ptype) {
|
||||
case BPF_PROG_TYPE_CGROUP_SKB:
|
||||
@@ -3932,7 +4081,7 @@ static int link_create(union bpf_attr *attr)
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
err_out:
|
||||
out:
|
||||
if (ret < 0)
|
||||
bpf_prog_put(prog);
|
||||
return ret;
|
||||
@@ -4014,9 +4163,31 @@ static int link_detach(union bpf_attr *attr)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int bpf_link_inc_not_zero(struct bpf_link *link)
|
||||
static struct bpf_link *bpf_link_inc_not_zero(struct bpf_link *link)
|
||||
{
|
||||
return atomic64_fetch_add_unless(&link->refcnt, 1, 0) ? 0 : -ENOENT;
|
||||
return atomic64_fetch_add_unless(&link->refcnt, 1, 0) ? link : ERR_PTR(-ENOENT);
|
||||
}
|
||||
|
||||
struct bpf_link *bpf_link_by_id(u32 id)
|
||||
{
|
||||
struct bpf_link *link;
|
||||
|
||||
if (!id)
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
spin_lock_bh(&link_idr_lock);
|
||||
/* before link is "settled", ID is 0, pretend it doesn't exist yet */
|
||||
link = idr_find(&link_idr, id);
|
||||
if (link) {
|
||||
if (link->id)
|
||||
link = bpf_link_inc_not_zero(link);
|
||||
else
|
||||
link = ERR_PTR(-EAGAIN);
|
||||
} else {
|
||||
link = ERR_PTR(-ENOENT);
|
||||
}
|
||||
spin_unlock_bh(&link_idr_lock);
|
||||
return link;
|
||||
}
|
||||
|
||||
#define BPF_LINK_GET_FD_BY_ID_LAST_FIELD link_id
|
||||
@@ -4025,7 +4196,7 @@ static int bpf_link_get_fd_by_id(const union bpf_attr *attr)
|
||||
{
|
||||
struct bpf_link *link;
|
||||
u32 id = attr->link_id;
|
||||
int fd, err;
|
||||
int fd;
|
||||
|
||||
if (CHECK_ATTR(BPF_LINK_GET_FD_BY_ID))
|
||||
return -EINVAL;
|
||||
@@ -4033,21 +4204,9 @@ static int bpf_link_get_fd_by_id(const union bpf_attr *attr)
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
spin_lock_bh(&link_idr_lock);
|
||||
link = idr_find(&link_idr, id);
|
||||
/* before link is "settled", ID is 0, pretend it doesn't exist yet */
|
||||
if (link) {
|
||||
if (link->id)
|
||||
err = bpf_link_inc_not_zero(link);
|
||||
else
|
||||
err = -EAGAIN;
|
||||
} else {
|
||||
err = -ENOENT;
|
||||
}
|
||||
spin_unlock_bh(&link_idr_lock);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
link = bpf_link_by_id(id);
|
||||
if (IS_ERR(link))
|
||||
return PTR_ERR(link);
|
||||
|
||||
fd = bpf_link_new_fd(link);
|
||||
if (fd < 0)
|
||||
@@ -4133,6 +4292,68 @@ static int bpf_iter_create(union bpf_attr *attr)
|
||||
return err;
|
||||
}
|
||||
|
||||
#define BPF_PROG_BIND_MAP_LAST_FIELD prog_bind_map.flags
|
||||
|
||||
static int bpf_prog_bind_map(union bpf_attr *attr)
|
||||
{
|
||||
struct bpf_prog *prog;
|
||||
struct bpf_map *map;
|
||||
struct bpf_map **used_maps_old, **used_maps_new;
|
||||
int i, ret = 0;
|
||||
|
||||
if (CHECK_ATTR(BPF_PROG_BIND_MAP))
|
||||
return -EINVAL;
|
||||
|
||||
if (attr->prog_bind_map.flags)
|
||||
return -EINVAL;
|
||||
|
||||
prog = bpf_prog_get(attr->prog_bind_map.prog_fd);
|
||||
if (IS_ERR(prog))
|
||||
return PTR_ERR(prog);
|
||||
|
||||
map = bpf_map_get(attr->prog_bind_map.map_fd);
|
||||
if (IS_ERR(map)) {
|
||||
ret = PTR_ERR(map);
|
||||
goto out_prog_put;
|
||||
}
|
||||
|
||||
mutex_lock(&prog->aux->used_maps_mutex);
|
||||
|
||||
used_maps_old = prog->aux->used_maps;
|
||||
|
||||
for (i = 0; i < prog->aux->used_map_cnt; i++)
|
||||
if (used_maps_old[i] == map) {
|
||||
bpf_map_put(map);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
used_maps_new = kmalloc_array(prog->aux->used_map_cnt + 1,
|
||||
sizeof(used_maps_new[0]),
|
||||
GFP_KERNEL);
|
||||
if (!used_maps_new) {
|
||||
ret = -ENOMEM;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
memcpy(used_maps_new, used_maps_old,
|
||||
sizeof(used_maps_old[0]) * prog->aux->used_map_cnt);
|
||||
used_maps_new[prog->aux->used_map_cnt] = map;
|
||||
|
||||
prog->aux->used_map_cnt++;
|
||||
prog->aux->used_maps = used_maps_new;
|
||||
|
||||
kfree(used_maps_old);
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&prog->aux->used_maps_mutex);
|
||||
|
||||
if (ret)
|
||||
bpf_map_put(map);
|
||||
out_prog_put:
|
||||
bpf_prog_put(prog);
|
||||
return ret;
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, size)
|
||||
{
|
||||
union bpf_attr attr;
|
||||
@@ -4266,6 +4487,9 @@ SYSCALL_DEFINE3(bpf, int, cmd, union bpf_attr __user *, uattr, unsigned int, siz
|
||||
case BPF_LINK_DETACH:
|
||||
err = link_detach(&attr);
|
||||
break;
|
||||
case BPF_PROG_BIND_MAP:
|
||||
err = bpf_prog_bind_map(&attr);
|
||||
break;
|
||||
default:
|
||||
err = -EINVAL;
|
||||
break;
|
||||
|
||||
@@ -30,15 +30,15 @@ static struct kobject *btf_kobj;
|
||||
|
||||
static int __init btf_vmlinux_init(void)
|
||||
{
|
||||
if (!__start_BTF)
|
||||
bin_attr_btf_vmlinux.size = __stop_BTF - __start_BTF;
|
||||
|
||||
if (!__start_BTF || bin_attr_btf_vmlinux.size == 0)
|
||||
return 0;
|
||||
|
||||
btf_kobj = kobject_create_and_add("btf", kernel_kobj);
|
||||
if (!btf_kobj)
|
||||
return -ENOMEM;
|
||||
|
||||
bin_attr_btf_vmlinux.size = __stop_BTF - __start_BTF;
|
||||
|
||||
return sysfs_create_bin_file(btf_kobj, &bin_attr_btf_vmlinux);
|
||||
}
|
||||
|
||||
|
||||
+15
-6
@@ -22,19 +22,27 @@ struct bpf_iter_seq_task_info {
|
||||
};
|
||||
|
||||
static struct task_struct *task_seq_get_next(struct pid_namespace *ns,
|
||||
u32 *tid)
|
||||
u32 *tid,
|
||||
bool skip_if_dup_files)
|
||||
{
|
||||
struct task_struct *task = NULL;
|
||||
struct pid *pid;
|
||||
|
||||
rcu_read_lock();
|
||||
retry:
|
||||
pid = idr_get_next(&ns->idr, tid);
|
||||
pid = find_ge_pid(*tid, ns);
|
||||
if (pid) {
|
||||
*tid = pid_nr_ns(pid, ns);
|
||||
task = get_pid_task(pid, PIDTYPE_PID);
|
||||
if (!task) {
|
||||
++*tid;
|
||||
goto retry;
|
||||
} else if (skip_if_dup_files && task->tgid != task->pid &&
|
||||
task->files == task->group_leader->files) {
|
||||
put_task_struct(task);
|
||||
task = NULL;
|
||||
++*tid;
|
||||
goto retry;
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
@@ -47,7 +55,7 @@ static void *task_seq_start(struct seq_file *seq, loff_t *pos)
|
||||
struct bpf_iter_seq_task_info *info = seq->private;
|
||||
struct task_struct *task;
|
||||
|
||||
task = task_seq_get_next(info->common.ns, &info->tid);
|
||||
task = task_seq_get_next(info->common.ns, &info->tid, false);
|
||||
if (!task)
|
||||
return NULL;
|
||||
|
||||
@@ -64,7 +72,7 @@ static void *task_seq_next(struct seq_file *seq, void *v, loff_t *pos)
|
||||
++*pos;
|
||||
++info->tid;
|
||||
put_task_struct((struct task_struct *)v);
|
||||
task = task_seq_get_next(info->common.ns, &info->tid);
|
||||
task = task_seq_get_next(info->common.ns, &info->tid, false);
|
||||
if (!task)
|
||||
return NULL;
|
||||
|
||||
@@ -147,7 +155,7 @@ again:
|
||||
curr_files = *fstruct;
|
||||
curr_fd = info->fd;
|
||||
} else {
|
||||
curr_task = task_seq_get_next(ns, &curr_tid);
|
||||
curr_task = task_seq_get_next(ns, &curr_tid, true);
|
||||
if (!curr_task)
|
||||
return NULL;
|
||||
|
||||
@@ -178,10 +186,11 @@ again:
|
||||
f = fcheck_files(curr_files, curr_fd);
|
||||
if (!f)
|
||||
continue;
|
||||
if (!get_file_rcu(f))
|
||||
continue;
|
||||
|
||||
/* set info->fd */
|
||||
info->fd = curr_fd;
|
||||
get_file(f);
|
||||
rcu_read_unlock();
|
||||
return f;
|
||||
}
|
||||
|
||||
+51
-12
@@ -7,6 +7,8 @@
|
||||
#include <linux/rbtree_latch.h>
|
||||
#include <linux/perf_event.h>
|
||||
#include <linux/btf.h>
|
||||
#include <linux/rcupdate_trace.h>
|
||||
#include <linux/rcupdate_wait.h>
|
||||
|
||||
/* dummy _ops. The verifier will operate on target program's ops. */
|
||||
const struct bpf_verifier_ops bpf_extension_verifier_ops = {
|
||||
@@ -63,7 +65,7 @@ static void bpf_trampoline_ksym_add(struct bpf_trampoline *tr)
|
||||
bpf_image_ksym_add(tr->image, ksym);
|
||||
}
|
||||
|
||||
struct bpf_trampoline *bpf_trampoline_lookup(u64 key)
|
||||
static struct bpf_trampoline *bpf_trampoline_lookup(u64 key)
|
||||
{
|
||||
struct bpf_trampoline *tr;
|
||||
struct hlist_head *head;
|
||||
@@ -210,9 +212,12 @@ static int bpf_trampoline_update(struct bpf_trampoline *tr)
|
||||
* updates to trampoline would change the code from underneath the
|
||||
* preempted task. Hence wait for tasks to voluntarily schedule or go
|
||||
* to userspace.
|
||||
* The same trampoline can hold both sleepable and non-sleepable progs.
|
||||
* synchronize_rcu_tasks_trace() is needed to make sure all sleepable
|
||||
* programs finish executing.
|
||||
* Wait for these two grace periods together.
|
||||
*/
|
||||
|
||||
synchronize_rcu_tasks();
|
||||
synchronize_rcu_mult(call_rcu_tasks, call_rcu_tasks_trace);
|
||||
|
||||
err = arch_prepare_bpf_trampoline(new_image, new_image + PAGE_SIZE / 2,
|
||||
&tr->func.model, flags, tprogs,
|
||||
@@ -256,14 +261,12 @@ static enum bpf_tramp_prog_type bpf_attach_type_to_tramp(struct bpf_prog *prog)
|
||||
}
|
||||
}
|
||||
|
||||
int bpf_trampoline_link_prog(struct bpf_prog *prog)
|
||||
int bpf_trampoline_link_prog(struct bpf_prog *prog, struct bpf_trampoline *tr)
|
||||
{
|
||||
enum bpf_tramp_prog_type kind;
|
||||
struct bpf_trampoline *tr;
|
||||
int err = 0;
|
||||
int cnt;
|
||||
|
||||
tr = prog->aux->trampoline;
|
||||
kind = bpf_attach_type_to_tramp(prog);
|
||||
mutex_lock(&tr->mutex);
|
||||
if (tr->extension_prog) {
|
||||
@@ -296,7 +299,7 @@ int bpf_trampoline_link_prog(struct bpf_prog *prog)
|
||||
}
|
||||
hlist_add_head(&prog->aux->tramp_hlist, &tr->progs_hlist[kind]);
|
||||
tr->progs_cnt[kind]++;
|
||||
err = bpf_trampoline_update(prog->aux->trampoline);
|
||||
err = bpf_trampoline_update(tr);
|
||||
if (err) {
|
||||
hlist_del(&prog->aux->tramp_hlist);
|
||||
tr->progs_cnt[kind]--;
|
||||
@@ -307,13 +310,11 @@ out:
|
||||
}
|
||||
|
||||
/* bpf_trampoline_unlink_prog() should never fail. */
|
||||
int bpf_trampoline_unlink_prog(struct bpf_prog *prog)
|
||||
int bpf_trampoline_unlink_prog(struct bpf_prog *prog, struct bpf_trampoline *tr)
|
||||
{
|
||||
enum bpf_tramp_prog_type kind;
|
||||
struct bpf_trampoline *tr;
|
||||
int err;
|
||||
|
||||
tr = prog->aux->trampoline;
|
||||
kind = bpf_attach_type_to_tramp(prog);
|
||||
mutex_lock(&tr->mutex);
|
||||
if (kind == BPF_TRAMP_REPLACE) {
|
||||
@@ -325,12 +326,32 @@ int bpf_trampoline_unlink_prog(struct bpf_prog *prog)
|
||||
}
|
||||
hlist_del(&prog->aux->tramp_hlist);
|
||||
tr->progs_cnt[kind]--;
|
||||
err = bpf_trampoline_update(prog->aux->trampoline);
|
||||
err = bpf_trampoline_update(tr);
|
||||
out:
|
||||
mutex_unlock(&tr->mutex);
|
||||
return err;
|
||||
}
|
||||
|
||||
struct bpf_trampoline *bpf_trampoline_get(u64 key,
|
||||
struct bpf_attach_target_info *tgt_info)
|
||||
{
|
||||
struct bpf_trampoline *tr;
|
||||
|
||||
tr = bpf_trampoline_lookup(key);
|
||||
if (!tr)
|
||||
return NULL;
|
||||
|
||||
mutex_lock(&tr->mutex);
|
||||
if (tr->func.addr)
|
||||
goto out;
|
||||
|
||||
memcpy(&tr->func.model, &tgt_info->fmodel, sizeof(tgt_info->fmodel));
|
||||
tr->func.addr = (void *)tgt_info->tgt_addr;
|
||||
out:
|
||||
mutex_unlock(&tr->mutex);
|
||||
return tr;
|
||||
}
|
||||
|
||||
void bpf_trampoline_put(struct bpf_trampoline *tr)
|
||||
{
|
||||
if (!tr)
|
||||
@@ -344,7 +365,14 @@ void bpf_trampoline_put(struct bpf_trampoline *tr)
|
||||
if (WARN_ON_ONCE(!hlist_empty(&tr->progs_hlist[BPF_TRAMP_FEXIT])))
|
||||
goto out;
|
||||
bpf_image_ksym_del(&tr->ksym);
|
||||
/* wait for tasks to get out of trampoline before freeing it */
|
||||
/* This code will be executed when all bpf progs (both sleepable and
|
||||
* non-sleepable) went through
|
||||
* bpf_prog_put()->call_rcu[_tasks_trace]()->bpf_prog_free_deferred().
|
||||
* Hence no need for another synchronize_rcu_tasks_trace() here,
|
||||
* but synchronize_rcu_tasks() is still needed, since trampoline
|
||||
* may not have had any sleepable programs and we need to wait
|
||||
* for tasks to get out of trampoline code before freeing it.
|
||||
*/
|
||||
synchronize_rcu_tasks();
|
||||
bpf_jit_free_exec(tr->image);
|
||||
hlist_del(&tr->hlist);
|
||||
@@ -394,6 +422,17 @@ void notrace __bpf_prog_exit(struct bpf_prog *prog, u64 start)
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
void notrace __bpf_prog_enter_sleepable(void)
|
||||
{
|
||||
rcu_read_lock_trace();
|
||||
might_fault();
|
||||
}
|
||||
|
||||
void notrace __bpf_prog_exit_sleepable(void)
|
||||
{
|
||||
rcu_read_unlock_trace();
|
||||
}
|
||||
|
||||
int __weak
|
||||
arch_prepare_bpf_trampoline(void *image, void *image_end,
|
||||
const struct btf_func_model *m, u32 flags,
|
||||
|
||||
+1068
-337
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -93,7 +93,7 @@ static int cap_validate_magic(cap_user_header_t header, unsigned *tocopy)
|
||||
break;
|
||||
case _LINUX_CAPABILITY_VERSION_2:
|
||||
warn_deprecated_v2();
|
||||
/* fall through - v3 is otherwise equivalent to v2. */
|
||||
fallthrough; /* v3 is otherwise equivalent to v2 */
|
||||
case _LINUX_CAPABILITY_VERSION_3:
|
||||
*tocopy = _LINUX_CAPABILITY_U32S_3;
|
||||
break;
|
||||
@@ -418,7 +418,7 @@ EXPORT_SYMBOL(ns_capable_noaudit);
|
||||
/**
|
||||
* ns_capable_setid - Determine if the current task has a superior capability
|
||||
* in effect, while signalling that this check is being done from within a
|
||||
* setid syscall.
|
||||
* setid or setgroups syscall.
|
||||
* @ns: The usernamespace we want the capability in
|
||||
* @cap: The capability to be tested for
|
||||
*
|
||||
|
||||
@@ -2006,7 +2006,6 @@ int cgroup_setup_root(struct cgroup_root *root, u16 ss_mask)
|
||||
BUG_ON(!list_empty(&root_cgrp->self.children));
|
||||
BUG_ON(atomic_read(&root->nr_cgrps) != 1);
|
||||
|
||||
kernfs_activate(root_cgrp->kn);
|
||||
ret = 0;
|
||||
goto out;
|
||||
|
||||
@@ -3682,6 +3681,9 @@ static ssize_t cgroup_file_write(struct kernfs_open_file *of, char *buf,
|
||||
struct cgroup_subsys_state *css;
|
||||
int ret;
|
||||
|
||||
if (!nbytes)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* If namespaces are delegation boundaries, disallow writes to
|
||||
* files in an non-init namespace root from inside the namespace
|
||||
|
||||
@@ -390,7 +390,7 @@ static void guarantee_online_cpus(struct cpuset *cs, struct cpumask *pmask)
|
||||
* The top cpuset doesn't have any online cpu as a
|
||||
* consequence of a race between cpuset_hotplug_work
|
||||
* and cpu hotplug notifier. But we know the top
|
||||
* cpuset's effective_cpus is on its way to to be
|
||||
* cpuset's effective_cpus is on its way to be
|
||||
* identical to cpu_online_mask.
|
||||
*/
|
||||
cpumask_copy(pmask, cpu_online_mask);
|
||||
|
||||
+3
-3
@@ -255,11 +255,11 @@ get_compat_sigset(sigset_t *set, const compat_sigset_t __user *compat)
|
||||
return -EFAULT;
|
||||
switch (_NSIG_WORDS) {
|
||||
case 4: set->sig[3] = v.sig[6] | (((long)v.sig[7]) << 32 );
|
||||
/* fall through */
|
||||
fallthrough;
|
||||
case 3: set->sig[2] = v.sig[4] | (((long)v.sig[5]) << 32 );
|
||||
/* fall through */
|
||||
fallthrough;
|
||||
case 2: set->sig[1] = v.sig[2] | (((long)v.sig[3]) << 32 );
|
||||
/* fall through */
|
||||
fallthrough;
|
||||
case 1: set->sig[0] = v.sig[0] | (((long)v.sig[1]) << 32 );
|
||||
}
|
||||
#else
|
||||
|
||||
+18
-30
@@ -15,18 +15,28 @@
|
||||
|
||||
static ATOMIC_NOTIFIER_HEAD(cpu_pm_notifier_chain);
|
||||
|
||||
static int cpu_pm_notify(enum cpu_pm_event event, int nr_to_call, int *nr_calls)
|
||||
static int cpu_pm_notify(enum cpu_pm_event event)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* __atomic_notifier_call_chain has a RCU read critical section, which
|
||||
* atomic_notifier_call_chain has a RCU read critical section, which
|
||||
* could be disfunctional in cpu idle. Copy RCU_NONIDLE code to let
|
||||
* RCU know this.
|
||||
*/
|
||||
rcu_irq_enter_irqson();
|
||||
ret = __atomic_notifier_call_chain(&cpu_pm_notifier_chain, event, NULL,
|
||||
nr_to_call, nr_calls);
|
||||
ret = atomic_notifier_call_chain(&cpu_pm_notifier_chain, event, NULL);
|
||||
rcu_irq_exit_irqson();
|
||||
|
||||
return notifier_to_errno(ret);
|
||||
}
|
||||
|
||||
static int cpu_pm_notify_robust(enum cpu_pm_event event_up, enum cpu_pm_event event_down)
|
||||
{
|
||||
int ret;
|
||||
|
||||
rcu_irq_enter_irqson();
|
||||
ret = atomic_notifier_call_chain_robust(&cpu_pm_notifier_chain, event_up, event_down, NULL);
|
||||
rcu_irq_exit_irqson();
|
||||
|
||||
return notifier_to_errno(ret);
|
||||
@@ -80,18 +90,7 @@ EXPORT_SYMBOL_GPL(cpu_pm_unregister_notifier);
|
||||
*/
|
||||
int cpu_pm_enter(void)
|
||||
{
|
||||
int nr_calls = 0;
|
||||
int ret = 0;
|
||||
|
||||
ret = cpu_pm_notify(CPU_PM_ENTER, -1, &nr_calls);
|
||||
if (ret)
|
||||
/*
|
||||
* Inform listeners (nr_calls - 1) about failure of CPU PM
|
||||
* PM entry who are notified earlier to prepare for it.
|
||||
*/
|
||||
cpu_pm_notify(CPU_PM_ENTER_FAILED, nr_calls - 1, NULL);
|
||||
|
||||
return ret;
|
||||
return cpu_pm_notify_robust(CPU_PM_ENTER, CPU_PM_ENTER_FAILED);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cpu_pm_enter);
|
||||
|
||||
@@ -109,7 +108,7 @@ EXPORT_SYMBOL_GPL(cpu_pm_enter);
|
||||
*/
|
||||
int cpu_pm_exit(void)
|
||||
{
|
||||
return cpu_pm_notify(CPU_PM_EXIT, -1, NULL);
|
||||
return cpu_pm_notify(CPU_PM_EXIT);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cpu_pm_exit);
|
||||
|
||||
@@ -131,18 +130,7 @@ EXPORT_SYMBOL_GPL(cpu_pm_exit);
|
||||
*/
|
||||
int cpu_cluster_pm_enter(void)
|
||||
{
|
||||
int nr_calls = 0;
|
||||
int ret = 0;
|
||||
|
||||
ret = cpu_pm_notify(CPU_CLUSTER_PM_ENTER, -1, &nr_calls);
|
||||
if (ret)
|
||||
/*
|
||||
* Inform listeners (nr_calls - 1) about failure of CPU cluster
|
||||
* PM entry who are notified earlier to prepare for it.
|
||||
*/
|
||||
cpu_pm_notify(CPU_CLUSTER_PM_ENTER_FAILED, nr_calls - 1, NULL);
|
||||
|
||||
return ret;
|
||||
return cpu_pm_notify_robust(CPU_CLUSTER_PM_ENTER, CPU_CLUSTER_PM_ENTER_FAILED);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cpu_cluster_pm_enter);
|
||||
|
||||
@@ -163,7 +151,7 @@ EXPORT_SYMBOL_GPL(cpu_cluster_pm_enter);
|
||||
*/
|
||||
int cpu_cluster_pm_exit(void)
|
||||
{
|
||||
return cpu_pm_notify(CPU_CLUSTER_PM_EXIT, -1, NULL);
|
||||
return cpu_pm_notify(CPU_CLUSTER_PM_EXIT);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cpu_cluster_pm_exit);
|
||||
|
||||
|
||||
+38
-10
@@ -80,7 +80,7 @@ static int exception_level;
|
||||
struct kgdb_io *dbg_io_ops;
|
||||
static DEFINE_SPINLOCK(kgdb_registration_lock);
|
||||
|
||||
/* Action for the reboot notifiter, a global allow kdb to change it */
|
||||
/* Action for the reboot notifier, a global allow kdb to change it */
|
||||
static int kgdbreboot;
|
||||
/* kgdb console driver is loaded */
|
||||
static int kgdb_con_registered;
|
||||
@@ -94,14 +94,6 @@ int dbg_switch_cpu;
|
||||
/* Use kdb or gdbserver mode */
|
||||
int dbg_kdb_mode = 1;
|
||||
|
||||
static int __init opt_kgdb_con(char *str)
|
||||
{
|
||||
kgdb_use_con = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
early_param("kgdbcon", opt_kgdb_con);
|
||||
|
||||
module_param(kgdb_use_con, int, 0644);
|
||||
module_param(kgdbreboot, int, 0644);
|
||||
|
||||
@@ -163,7 +155,7 @@ early_param("nokgdbroundup", opt_nokgdbroundup);
|
||||
|
||||
/*
|
||||
* Weak aliases for breakpoint management,
|
||||
* can be overriden by architectures when needed:
|
||||
* can be overridden by architectures when needed:
|
||||
*/
|
||||
int __weak kgdb_arch_set_breakpoint(struct kgdb_bkpt *bpt)
|
||||
{
|
||||
@@ -177,17 +169,23 @@ int __weak kgdb_arch_set_breakpoint(struct kgdb_bkpt *bpt)
|
||||
arch_kgdb_ops.gdb_bpt_instr, BREAK_INSTR_SIZE);
|
||||
return err;
|
||||
}
|
||||
NOKPROBE_SYMBOL(kgdb_arch_set_breakpoint);
|
||||
|
||||
int __weak kgdb_arch_remove_breakpoint(struct kgdb_bkpt *bpt)
|
||||
{
|
||||
return copy_to_kernel_nofault((char *)bpt->bpt_addr,
|
||||
(char *)bpt->saved_instr, BREAK_INSTR_SIZE);
|
||||
}
|
||||
NOKPROBE_SYMBOL(kgdb_arch_remove_breakpoint);
|
||||
|
||||
int __weak kgdb_validate_break_address(unsigned long addr)
|
||||
{
|
||||
struct kgdb_bkpt tmp;
|
||||
int err;
|
||||
|
||||
if (kgdb_within_blocklist(addr))
|
||||
return -EINVAL;
|
||||
|
||||
/* Validate setting the breakpoint and then removing it. If the
|
||||
* remove fails, the kernel needs to emit a bad message because we
|
||||
* are deep trouble not being able to put things back the way we
|
||||
@@ -208,6 +206,7 @@ unsigned long __weak kgdb_arch_pc(int exception, struct pt_regs *regs)
|
||||
{
|
||||
return instruction_pointer(regs);
|
||||
}
|
||||
NOKPROBE_SYMBOL(kgdb_arch_pc);
|
||||
|
||||
int __weak kgdb_arch_init(void)
|
||||
{
|
||||
@@ -218,6 +217,7 @@ int __weak kgdb_skipexception(int exception, struct pt_regs *regs)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
NOKPROBE_SYMBOL(kgdb_skipexception);
|
||||
|
||||
#ifdef CONFIG_SMP
|
||||
|
||||
@@ -239,6 +239,7 @@ void __weak kgdb_call_nmi_hook(void *ignored)
|
||||
*/
|
||||
kgdb_nmicallback(raw_smp_processor_id(), get_irq_regs());
|
||||
}
|
||||
NOKPROBE_SYMBOL(kgdb_call_nmi_hook);
|
||||
|
||||
void __weak kgdb_roundup_cpus(void)
|
||||
{
|
||||
@@ -272,6 +273,7 @@ void __weak kgdb_roundup_cpus(void)
|
||||
kgdb_info[cpu].rounding_up = false;
|
||||
}
|
||||
}
|
||||
NOKPROBE_SYMBOL(kgdb_roundup_cpus);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -298,6 +300,7 @@ static void kgdb_flush_swbreak_addr(unsigned long addr)
|
||||
/* Force flush instruction cache if it was outside the mm */
|
||||
flush_icache_range(addr, addr + BREAK_INSTR_SIZE);
|
||||
}
|
||||
NOKPROBE_SYMBOL(kgdb_flush_swbreak_addr);
|
||||
|
||||
/*
|
||||
* SW breakpoint management:
|
||||
@@ -325,6 +328,7 @@ int dbg_activate_sw_breakpoints(void)
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
NOKPROBE_SYMBOL(dbg_activate_sw_breakpoints);
|
||||
|
||||
int dbg_set_sw_break(unsigned long addr)
|
||||
{
|
||||
@@ -388,6 +392,7 @@ int dbg_deactivate_sw_breakpoints(void)
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
NOKPROBE_SYMBOL(dbg_deactivate_sw_breakpoints);
|
||||
|
||||
int dbg_remove_sw_break(unsigned long addr)
|
||||
{
|
||||
@@ -509,6 +514,7 @@ static int kgdb_io_ready(int print_wait)
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
NOKPROBE_SYMBOL(kgdb_io_ready);
|
||||
|
||||
static int kgdb_reenter_check(struct kgdb_state *ks)
|
||||
{
|
||||
@@ -556,6 +562,7 @@ static int kgdb_reenter_check(struct kgdb_state *ks)
|
||||
|
||||
return 1;
|
||||
}
|
||||
NOKPROBE_SYMBOL(kgdb_reenter_check);
|
||||
|
||||
static void dbg_touch_watchdogs(void)
|
||||
{
|
||||
@@ -563,6 +570,7 @@ static void dbg_touch_watchdogs(void)
|
||||
clocksource_touch_watchdog();
|
||||
rcu_cpu_stall_reset();
|
||||
}
|
||||
NOKPROBE_SYMBOL(dbg_touch_watchdogs);
|
||||
|
||||
static int kgdb_cpu_enter(struct kgdb_state *ks, struct pt_regs *regs,
|
||||
int exception_state)
|
||||
@@ -752,6 +760,8 @@ cpu_master_loop:
|
||||
}
|
||||
}
|
||||
|
||||
dbg_activate_sw_breakpoints();
|
||||
|
||||
/* Call the I/O driver's post_exception routine */
|
||||
if (dbg_io_ops->post_exception)
|
||||
dbg_io_ops->post_exception();
|
||||
@@ -794,6 +804,7 @@ kgdb_restore:
|
||||
|
||||
return kgdb_info[cpu].ret_state;
|
||||
}
|
||||
NOKPROBE_SYMBOL(kgdb_cpu_enter);
|
||||
|
||||
/*
|
||||
* kgdb_handle_exception() - main entry point from a kernel exception
|
||||
@@ -838,6 +849,7 @@ out:
|
||||
arch_kgdb_ops.enable_nmi(1);
|
||||
return ret;
|
||||
}
|
||||
NOKPROBE_SYMBOL(kgdb_handle_exception);
|
||||
|
||||
/*
|
||||
* GDB places a breakpoint at this function to know dynamically loaded objects.
|
||||
@@ -872,6 +884,7 @@ int kgdb_nmicallback(int cpu, void *regs)
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
NOKPROBE_SYMBOL(kgdb_nmicallback);
|
||||
|
||||
int kgdb_nmicallin(int cpu, int trapnr, void *regs, int err_code,
|
||||
atomic_t *send_ready)
|
||||
@@ -897,6 +910,7 @@ int kgdb_nmicallin(int cpu, int trapnr, void *regs, int err_code,
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
NOKPROBE_SYMBOL(kgdb_nmicallin);
|
||||
|
||||
static void kgdb_console_write(struct console *co, const char *s,
|
||||
unsigned count)
|
||||
@@ -920,6 +934,20 @@ static struct console kgdbcons = {
|
||||
.index = -1,
|
||||
};
|
||||
|
||||
static int __init opt_kgdb_con(char *str)
|
||||
{
|
||||
kgdb_use_con = 1;
|
||||
|
||||
if (kgdb_io_module_registered && !kgdb_con_registered) {
|
||||
register_console(&kgdbcons);
|
||||
kgdb_con_registered = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
early_param("kgdbcon", opt_kgdb_con);
|
||||
|
||||
#ifdef CONFIG_MAGIC_SYSRQ
|
||||
static void sysrq_handle_dbg(int key)
|
||||
{
|
||||
|
||||
@@ -725,7 +725,7 @@ static void gdb_cmd_query(struct kgdb_state *ks)
|
||||
}
|
||||
}
|
||||
|
||||
do_each_thread(g, p) {
|
||||
for_each_process_thread(g, p) {
|
||||
if (i >= ks->thr_query && !finished) {
|
||||
int_to_threadref(thref, p->pid);
|
||||
ptr = pack_threadid(ptr, thref);
|
||||
@@ -735,7 +735,7 @@ static void gdb_cmd_query(struct kgdb_state *ks)
|
||||
finished = 1;
|
||||
}
|
||||
i++;
|
||||
} while_each_thread(g, p);
|
||||
}
|
||||
|
||||
*(--ptr) = '\0';
|
||||
break;
|
||||
@@ -1046,14 +1046,14 @@ int gdb_serial_stub(struct kgdb_state *ks)
|
||||
return DBG_PASS_EVENT;
|
||||
}
|
||||
#endif
|
||||
/* Fall through */
|
||||
fallthrough;
|
||||
case 'C': /* Exception passing */
|
||||
tmp = gdb_cmd_exception_pass(ks);
|
||||
if (tmp > 0)
|
||||
goto default_handle;
|
||||
if (tmp == 0)
|
||||
break;
|
||||
/* Fall through - on tmp < 0 */
|
||||
fallthrough; /* on tmp < 0 */
|
||||
case 'c': /* Continue packet */
|
||||
case 's': /* Single step packet */
|
||||
if (kgdb_contthread && kgdb_contthread != current) {
|
||||
@@ -1061,8 +1061,7 @@ int gdb_serial_stub(struct kgdb_state *ks)
|
||||
error_packet(remcom_out_buffer, -EINVAL);
|
||||
break;
|
||||
}
|
||||
dbg_activate_sw_breakpoints();
|
||||
/* Fall through - to default processing */
|
||||
fallthrough; /* to default processing */
|
||||
default:
|
||||
default_handle:
|
||||
error = kgdb_arch_handle_exception(ks->ex_vector,
|
||||
|
||||
@@ -306,6 +306,15 @@ static int kdb_bp(int argc, const char **argv)
|
||||
if (!template.bp_addr)
|
||||
return KDB_BADINT;
|
||||
|
||||
/*
|
||||
* This check is redundant (since the breakpoint machinery should
|
||||
* be doing the same check during kdb_bp_install) but gives the
|
||||
* user immediate feedback.
|
||||
*/
|
||||
diag = kgdb_validate_break_address(template.bp_addr);
|
||||
if (diag)
|
||||
return diag;
|
||||
|
||||
/*
|
||||
* Find an empty bp structure to allocate
|
||||
*/
|
||||
|
||||
@@ -149,14 +149,14 @@ kdb_bt(int argc, const char **argv)
|
||||
return 0;
|
||||
}
|
||||
/* Now the inactive tasks */
|
||||
kdb_do_each_thread(g, p) {
|
||||
for_each_process_thread(g, p) {
|
||||
if (KDB_FLAG(CMD_INTERRUPT))
|
||||
return 0;
|
||||
if (task_curr(p))
|
||||
continue;
|
||||
if (kdb_bt1(p, mask, btaprompt))
|
||||
return 0;
|
||||
} kdb_while_each_thread(g, p);
|
||||
}
|
||||
} else if (strcmp(argv[0], "btp") == 0) {
|
||||
struct task_struct *p;
|
||||
unsigned long pid;
|
||||
|
||||
@@ -147,7 +147,6 @@ int kdb_stub(struct kgdb_state *ks)
|
||||
return DBG_PASS_EVENT;
|
||||
}
|
||||
kdb_bp_install(ks->linux_regs);
|
||||
dbg_activate_sw_breakpoints();
|
||||
/* Set the exit state to a single step or a continue */
|
||||
if (KDB_STATE(DOING_SS))
|
||||
gdbstub_state(ks, "s");
|
||||
@@ -167,7 +166,6 @@ int kdb_stub(struct kgdb_state *ks)
|
||||
* differently vs the gdbstub
|
||||
*/
|
||||
kgdb_single_step = 0;
|
||||
dbg_deactivate_sw_breakpoints();
|
||||
return DBG_SWITCH_CPU_EVENT;
|
||||
}
|
||||
return kgdb_info[ks->cpu].ret_state;
|
||||
|
||||
@@ -545,18 +545,18 @@ static int kdb_search_string(char *searched, char *searchfor)
|
||||
static void kdb_msg_write(const char *msg, int msg_len)
|
||||
{
|
||||
struct console *c;
|
||||
const char *cp;
|
||||
int len;
|
||||
|
||||
if (msg_len == 0)
|
||||
return;
|
||||
|
||||
if (dbg_io_ops) {
|
||||
const char *cp = msg;
|
||||
int len = msg_len;
|
||||
cp = msg;
|
||||
len = msg_len;
|
||||
|
||||
while (len--) {
|
||||
dbg_io_ops->write_char(*cp);
|
||||
cp++;
|
||||
}
|
||||
while (len--) {
|
||||
dbg_io_ops->write_char(*cp);
|
||||
cp++;
|
||||
}
|
||||
|
||||
for_each_console(c) {
|
||||
@@ -706,12 +706,16 @@ int vkdb_printf(enum kdb_msgsrc src, const char *fmt, va_list ap)
|
||||
size_avail = sizeof(kdb_buffer) - len;
|
||||
goto kdb_print_out;
|
||||
}
|
||||
if (kdb_grepping_flag >= KDB_GREPPING_FLAG_SEARCH)
|
||||
if (kdb_grepping_flag >= KDB_GREPPING_FLAG_SEARCH) {
|
||||
/*
|
||||
* This was a interactive search (using '/' at more
|
||||
* prompt) and it has completed. Clear the flag.
|
||||
* prompt) and it has completed. Replace the \0 with
|
||||
* its original value to ensure multi-line strings
|
||||
* are handled properly, and return to normal mode.
|
||||
*/
|
||||
*cphold = replaced_byte;
|
||||
kdb_grepping_flag = 0;
|
||||
}
|
||||
/*
|
||||
* at this point the string is a full line and
|
||||
* should be printed, up to the null.
|
||||
|
||||
@@ -173,11 +173,11 @@ int kdb_get_kbd_char(void)
|
||||
case KT_LATIN:
|
||||
if (isprint(keychar))
|
||||
break; /* printable characters */
|
||||
/* fall through */
|
||||
fallthrough;
|
||||
case KT_SPEC:
|
||||
if (keychar == K_ENTER)
|
||||
break;
|
||||
/* fall through */
|
||||
fallthrough;
|
||||
default:
|
||||
return -1; /* ignore unprintables */
|
||||
}
|
||||
|
||||
@@ -2299,10 +2299,10 @@ void kdb_ps_suppressed(void)
|
||||
if (kdb_task_state(p, mask_I))
|
||||
++idle;
|
||||
}
|
||||
kdb_do_each_thread(g, p) {
|
||||
for_each_process_thread(g, p) {
|
||||
if (kdb_task_state(p, mask_M))
|
||||
++daemon;
|
||||
} kdb_while_each_thread(g, p);
|
||||
}
|
||||
if (idle || daemon) {
|
||||
if (idle)
|
||||
kdb_printf("%d idle process%s (state I)%s\n",
|
||||
@@ -2370,12 +2370,12 @@ static int kdb_ps(int argc, const char **argv)
|
||||
}
|
||||
kdb_printf("\n");
|
||||
/* Now the real tasks */
|
||||
kdb_do_each_thread(g, p) {
|
||||
for_each_process_thread(g, p) {
|
||||
if (KDB_FLAG(CMD_INTERRUPT))
|
||||
return 0;
|
||||
if (kdb_task_state(p, mask))
|
||||
kdb_ps1(p);
|
||||
} kdb_while_each_thread(g, p);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -230,10 +230,6 @@ extern struct task_struct *kdb_curr_task(int);
|
||||
|
||||
#define kdb_task_has_cpu(p) (task_curr(p))
|
||||
|
||||
/* Simplify coexistence with NPTL */
|
||||
#define kdb_do_each_thread(g, p) do_each_thread(g, p)
|
||||
#define kdb_while_each_thread(g, p) while_each_thread(g, p)
|
||||
|
||||
#define GFP_KDB (in_interrupt() ? GFP_ATOMIC : GFP_KERNEL)
|
||||
|
||||
extern void *debug_kmalloc(size_t size, gfp_t flags);
|
||||
|
||||
@@ -432,7 +432,7 @@ int kdb_getphysword(unsigned long *word, unsigned long addr, size_t size)
|
||||
*word = w8;
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
fallthrough;
|
||||
default:
|
||||
diag = KDB_BADWIDTH;
|
||||
kdb_printf("kdb_getphysword: bad width %ld\n", (long) size);
|
||||
@@ -481,7 +481,7 @@ int kdb_getword(unsigned long *word, unsigned long addr, size_t size)
|
||||
*word = w8;
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
fallthrough;
|
||||
default:
|
||||
diag = KDB_BADWIDTH;
|
||||
kdb_printf("kdb_getword: bad width %ld\n", (long) size);
|
||||
@@ -525,7 +525,7 @@ int kdb_putword(unsigned long addr, unsigned long word, size_t size)
|
||||
diag = kdb_putarea(addr, w8);
|
||||
break;
|
||||
}
|
||||
/* fall through */
|
||||
fallthrough;
|
||||
default:
|
||||
diag = KDB_BADWIDTH;
|
||||
kdb_printf("kdb_putword: bad width %ld\n", (long) size);
|
||||
|
||||
+20
-5
@@ -9,6 +9,7 @@ config HAS_DMA
|
||||
default y
|
||||
|
||||
config DMA_OPS
|
||||
depends on HAS_DMA
|
||||
bool
|
||||
|
||||
#
|
||||
@@ -43,6 +44,12 @@ config ARCH_HAS_DMA_SET_MASK
|
||||
config ARCH_HAS_DMA_WRITE_COMBINE
|
||||
bool
|
||||
|
||||
#
|
||||
# Select if the architectures provides the arch_dma_mark_clean hook
|
||||
#
|
||||
config ARCH_HAS_DMA_MARK_CLEAN
|
||||
bool
|
||||
|
||||
config DMA_DECLARE_COHERENT
|
||||
bool
|
||||
|
||||
@@ -68,9 +75,6 @@ config ARCH_HAS_DMA_PREP_COHERENT
|
||||
config ARCH_HAS_FORCE_DMA_UNENCRYPTED
|
||||
bool
|
||||
|
||||
config DMA_NONCOHERENT_CACHE_SYNC
|
||||
bool
|
||||
|
||||
config DMA_VIRT_OPS
|
||||
bool
|
||||
depends on HAS_DMA
|
||||
@@ -114,10 +118,21 @@ config DMA_CMA
|
||||
You can disable CMA by specifying "cma=0" on the kernel's command
|
||||
line.
|
||||
|
||||
For more information see <include/linux/dma-contiguous.h>.
|
||||
For more information see <kernel/dma/contiguous.c>.
|
||||
If unsure, say "n".
|
||||
|
||||
if DMA_CMA
|
||||
|
||||
config DMA_PERNUMA_CMA
|
||||
bool "Enable separate DMA Contiguous Memory Area for each NUMA Node"
|
||||
default NUMA && ARM64
|
||||
help
|
||||
Enable this option to get pernuma CMA areas so that devices like
|
||||
ARM64 SMMU can get local memory by DMA coherent APIs.
|
||||
|
||||
You can set the size of pernuma CMA by specifying "cma_pernuma=size"
|
||||
on the kernel's command line.
|
||||
|
||||
comment "Default contiguous memory area size:"
|
||||
|
||||
config CMA_SIZE_MBYTES
|
||||
@@ -162,7 +177,7 @@ endchoice
|
||||
|
||||
config CMA_ALIGNMENT
|
||||
int "Maximum PAGE_SIZE order of alignment for contiguous buffers"
|
||||
range 4 12
|
||||
range 2 12
|
||||
default 8
|
||||
help
|
||||
DMA mapping framework by default aligns all buffers to the smallest
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
obj-$(CONFIG_HAS_DMA) += mapping.o direct.o
|
||||
obj-$(CONFIG_DMA_OPS) += ops_helpers.o
|
||||
obj-$(CONFIG_DMA_OPS) += dummy.o
|
||||
obj-$(CONFIG_DMA_CMA) += contiguous.o
|
||||
obj-$(CONFIG_DMA_DECLARE_COHERENT) += coherent.o
|
||||
|
||||
+21
-4
@@ -7,7 +7,8 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dma-direct.h>
|
||||
#include <linux/dma-map-ops.h>
|
||||
|
||||
struct dma_coherent_mem {
|
||||
void *virt_base;
|
||||
@@ -32,9 +33,8 @@ static inline dma_addr_t dma_get_device_base(struct device *dev,
|
||||
struct dma_coherent_mem * mem)
|
||||
{
|
||||
if (mem->use_dev_dma_pfn_offset)
|
||||
return (mem->pfn_base - dev->dma_pfn_offset) << PAGE_SHIFT;
|
||||
else
|
||||
return mem->device_base;
|
||||
return phys_to_dma(dev, PFN_PHYS(mem->pfn_base));
|
||||
return mem->device_base;
|
||||
}
|
||||
|
||||
static int dma_init_coherent_memory(phys_addr_t phys_addr,
|
||||
@@ -107,6 +107,23 @@ static int dma_assign_coherent_memory(struct device *dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Declare a region of memory to be handed out by dma_alloc_coherent() when it
|
||||
* is asked for coherent memory for this device. This shall only be used
|
||||
* from platform code, usually based on the device tree description.
|
||||
*
|
||||
* phys_addr is the CPU physical address to which the memory is currently
|
||||
* assigned (this will be ioremapped so the CPU can access the region).
|
||||
*
|
||||
* device_addr is the DMA address the device needs to be programmed with to
|
||||
* actually address this memory (this will be handed out as the dma_addr_t in
|
||||
* dma_alloc_coherent()).
|
||||
*
|
||||
* size is the size of the area (must be a multiple of PAGE_SIZE).
|
||||
*
|
||||
* As a simplification for the platforms, only *one* such region of memory may
|
||||
* be declared per device.
|
||||
*/
|
||||
int dma_declare_coherent_memory(struct device *dev, phys_addr_t phys_addr,
|
||||
dma_addr_t device_addr, size_t size)
|
||||
{
|
||||
|
||||
+128
-25
@@ -5,6 +5,34 @@
|
||||
* Written by:
|
||||
* Marek Szyprowski <m.szyprowski@samsung.com>
|
||||
* Michal Nazarewicz <mina86@mina86.com>
|
||||
*
|
||||
* Contiguous Memory Allocator
|
||||
*
|
||||
* The Contiguous Memory Allocator (CMA) makes it possible to
|
||||
* allocate big contiguous chunks of memory after the system has
|
||||
* booted.
|
||||
*
|
||||
* Why is it needed?
|
||||
*
|
||||
* Various devices on embedded systems have no scatter-getter and/or
|
||||
* IO map support and require contiguous blocks of memory to
|
||||
* operate. They include devices such as cameras, hardware video
|
||||
* coders, etc.
|
||||
*
|
||||
* Such devices often require big memory buffers (a full HD frame
|
||||
* is, for instance, more then 2 mega pixels large, i.e. more than 6
|
||||
* MB of memory), which makes mechanisms such as kmalloc() or
|
||||
* alloc_page() ineffective.
|
||||
*
|
||||
* At the same time, a solution where a big memory region is
|
||||
* reserved for a device is suboptimal since often more memory is
|
||||
* reserved then strictly required and, moreover, the memory is
|
||||
* inaccessible to page system even if device drivers don't use it.
|
||||
*
|
||||
* CMA tries to solve this issue by operating on memory regions
|
||||
* where only movable pages can be allocated from. This way, kernel
|
||||
* can use the memory for pagecache and when device driver requests
|
||||
* it, allocated pages can be migrated.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "cma: " fmt
|
||||
@@ -16,12 +44,11 @@
|
||||
#endif
|
||||
|
||||
#include <asm/page.h>
|
||||
#include <asm/dma-contiguous.h>
|
||||
|
||||
#include <linux/memblock.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/dma-contiguous.h>
|
||||
#include <linux/dma-map-ops.h>
|
||||
#include <linux/cma.h>
|
||||
|
||||
#ifdef CONFIG_CMA_SIZE_MBYTES
|
||||
@@ -69,20 +96,24 @@ static int __init early_cma(char *p)
|
||||
}
|
||||
early_param("cma", early_cma);
|
||||
|
||||
#ifdef CONFIG_DMA_PERNUMA_CMA
|
||||
|
||||
static struct cma *dma_contiguous_pernuma_area[MAX_NUMNODES];
|
||||
static phys_addr_t pernuma_size_bytes __initdata;
|
||||
|
||||
static int __init early_cma_pernuma(char *p)
|
||||
{
|
||||
pernuma_size_bytes = memparse(p, &p);
|
||||
return 0;
|
||||
}
|
||||
early_param("cma_pernuma", early_cma_pernuma);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_CMA_SIZE_PERCENTAGE
|
||||
|
||||
static phys_addr_t __init __maybe_unused cma_early_percent_memory(void)
|
||||
{
|
||||
struct memblock_region *reg;
|
||||
unsigned long total_pages = 0;
|
||||
|
||||
/*
|
||||
* We cannot use memblock_phys_mem_size() here, because
|
||||
* memblock_analyze() has not been called yet.
|
||||
*/
|
||||
for_each_memblock(memory, reg)
|
||||
total_pages += memblock_region_memory_end_pfn(reg) -
|
||||
memblock_region_memory_base_pfn(reg);
|
||||
unsigned long total_pages = PHYS_PFN(memblock_phys_mem_size());
|
||||
|
||||
return (total_pages * CONFIG_CMA_SIZE_PERCENTAGE / 100) << PAGE_SHIFT;
|
||||
}
|
||||
@@ -96,6 +127,34 @@ static inline __maybe_unused phys_addr_t cma_early_percent_memory(void)
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DMA_PERNUMA_CMA
|
||||
void __init dma_pernuma_cma_reserve(void)
|
||||
{
|
||||
int nid;
|
||||
|
||||
if (!pernuma_size_bytes)
|
||||
return;
|
||||
|
||||
for_each_online_node(nid) {
|
||||
int ret;
|
||||
char name[CMA_MAX_NAME];
|
||||
struct cma **cma = &dma_contiguous_pernuma_area[nid];
|
||||
|
||||
snprintf(name, sizeof(name), "pernuma%d", nid);
|
||||
ret = cma_declare_contiguous_nid(0, pernuma_size_bytes, 0, 0,
|
||||
0, false, name, cma, nid);
|
||||
if (ret) {
|
||||
pr_warn("%s: reservation failed: err %d, node %d", __func__,
|
||||
ret, nid);
|
||||
continue;
|
||||
}
|
||||
|
||||
pr_debug("%s: reserved %llu MiB on node %d\n", __func__,
|
||||
(unsigned long long)pernuma_size_bytes / SZ_1M, nid);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* dma_contiguous_reserve() - reserve area(s) for contiguous memory handling
|
||||
* @limit: End address of the reserved memory (optional, 0 for any).
|
||||
@@ -143,6 +202,11 @@ void __init dma_contiguous_reserve(phys_addr_t limit)
|
||||
}
|
||||
}
|
||||
|
||||
void __weak
|
||||
dma_contiguous_early_fixup(phys_addr_t base, unsigned long size)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* dma_contiguous_reserve_area() - reserve custom contiguous area
|
||||
* @size: Size of the reserved area (in bytes),
|
||||
@@ -228,23 +292,44 @@ static struct page *cma_alloc_aligned(struct cma *cma, size_t size, gfp_t gfp)
|
||||
* @size: Requested allocation size.
|
||||
* @gfp: Allocation flags.
|
||||
*
|
||||
* This function allocates contiguous memory buffer for specified device. It
|
||||
* tries to use device specific contiguous memory area if available, or the
|
||||
* default global one.
|
||||
* tries to use device specific contiguous memory area if available, or it
|
||||
* tries to use per-numa cma, if the allocation fails, it will fallback to
|
||||
* try default global one.
|
||||
*
|
||||
* Note that it byapss one-page size of allocations from the global area as
|
||||
* the addresses within one page are always contiguous, so there is no need
|
||||
* to waste CMA pages for that kind; it also helps reduce fragmentations.
|
||||
* Note that it bypass one-page size of allocations from the per-numa and
|
||||
* global area as the addresses within one page are always contiguous, so
|
||||
* there is no need to waste CMA pages for that kind; it also helps reduce
|
||||
* fragmentations.
|
||||
*/
|
||||
struct page *dma_alloc_contiguous(struct device *dev, size_t size, gfp_t gfp)
|
||||
{
|
||||
#ifdef CONFIG_DMA_PERNUMA_CMA
|
||||
int nid = dev_to_node(dev);
|
||||
#endif
|
||||
|
||||
/* CMA can be used only in the context which permits sleeping */
|
||||
if (!gfpflags_allow_blocking(gfp))
|
||||
return NULL;
|
||||
if (dev->cma_area)
|
||||
return cma_alloc_aligned(dev->cma_area, size, gfp);
|
||||
if (size <= PAGE_SIZE || !dma_contiguous_default_area)
|
||||
if (size <= PAGE_SIZE)
|
||||
return NULL;
|
||||
|
||||
#ifdef CONFIG_DMA_PERNUMA_CMA
|
||||
if (nid != NUMA_NO_NODE && !(gfp & (GFP_DMA | GFP_DMA32))) {
|
||||
struct cma *cma = dma_contiguous_pernuma_area[nid];
|
||||
struct page *page;
|
||||
|
||||
if (cma) {
|
||||
page = cma_alloc_aligned(cma, size, gfp);
|
||||
if (page)
|
||||
return page;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (!dma_contiguous_default_area)
|
||||
return NULL;
|
||||
|
||||
return cma_alloc_aligned(dma_contiguous_default_area, size, gfp);
|
||||
}
|
||||
|
||||
@@ -261,9 +346,27 @@ struct page *dma_alloc_contiguous(struct device *dev, size_t size, gfp_t gfp)
|
||||
*/
|
||||
void dma_free_contiguous(struct device *dev, struct page *page, size_t size)
|
||||
{
|
||||
if (!cma_release(dev_get_cma_area(dev), page,
|
||||
PAGE_ALIGN(size) >> PAGE_SHIFT))
|
||||
__free_pages(page, get_order(size));
|
||||
unsigned int count = PAGE_ALIGN(size) >> PAGE_SHIFT;
|
||||
|
||||
/* if dev has its own cma, free page from there */
|
||||
if (dev->cma_area) {
|
||||
if (cma_release(dev->cma_area, page, count))
|
||||
return;
|
||||
} else {
|
||||
/*
|
||||
* otherwise, page is from either per-numa cma or default cma
|
||||
*/
|
||||
#ifdef CONFIG_DMA_PERNUMA_CMA
|
||||
if (cma_release(dma_contiguous_pernuma_area[page_to_nid(page)],
|
||||
page, count))
|
||||
return;
|
||||
#endif
|
||||
if (cma_release(dma_contiguous_default_area, page, count))
|
||||
return;
|
||||
}
|
||||
|
||||
/* not in any cma, free from buddy */
|
||||
__free_pages(page, get_order(size));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -279,14 +382,14 @@ void dma_free_contiguous(struct device *dev, struct page *page, size_t size)
|
||||
|
||||
static int rmem_cma_device_init(struct reserved_mem *rmem, struct device *dev)
|
||||
{
|
||||
dev_set_cma_area(dev, rmem->priv);
|
||||
dev->cma_area = rmem->priv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rmem_cma_device_release(struct reserved_mem *rmem,
|
||||
struct device *dev)
|
||||
{
|
||||
dev_set_cma_area(dev, NULL);
|
||||
dev->cma_area = NULL;
|
||||
}
|
||||
|
||||
static const struct reserved_mem_ops rmem_cma_ops = {
|
||||
@@ -327,7 +430,7 @@ static int __init rmem_cma_setup(struct reserved_mem *rmem)
|
||||
dma_contiguous_early_fixup(rmem->base, rmem->size);
|
||||
|
||||
if (default_cma)
|
||||
dma_contiguous_set_default(cma);
|
||||
dma_contiguous_default_area = cma;
|
||||
|
||||
rmem->ops = &rmem_cma_ops;
|
||||
rmem->priv = cma;
|
||||
|
||||
+4
-15
@@ -9,10 +9,9 @@
|
||||
|
||||
#include <linux/sched/task_stack.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dma-map-ops.h>
|
||||
#include <linux/sched/task.h>
|
||||
#include <linux/stacktrace.h>
|
||||
#include <linux/dma-debug.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/debugfs.h>
|
||||
@@ -24,8 +23,8 @@
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <asm/sections.h>
|
||||
#include "debug.h"
|
||||
|
||||
#define HASH_SIZE 16384ULL
|
||||
#define HASH_FN_SHIFT 13
|
||||
@@ -1219,7 +1218,7 @@ void debug_dma_map_page(struct device *dev, struct page *page, size_t offset,
|
||||
entry->dev = dev;
|
||||
entry->type = dma_debug_single;
|
||||
entry->pfn = page_to_pfn(page);
|
||||
entry->offset = offset,
|
||||
entry->offset = offset;
|
||||
entry->dev_addr = dma_addr;
|
||||
entry->size = size;
|
||||
entry->direction = direction;
|
||||
@@ -1235,7 +1234,6 @@ void debug_dma_map_page(struct device *dev, struct page *page, size_t offset,
|
||||
|
||||
add_dma_entry(entry);
|
||||
}
|
||||
EXPORT_SYMBOL(debug_dma_map_page);
|
||||
|
||||
void debug_dma_mapping_error(struct device *dev, dma_addr_t dma_addr)
|
||||
{
|
||||
@@ -1290,7 +1288,6 @@ void debug_dma_unmap_page(struct device *dev, dma_addr_t addr,
|
||||
return;
|
||||
check_unmap(&ref);
|
||||
}
|
||||
EXPORT_SYMBOL(debug_dma_unmap_page);
|
||||
|
||||
void debug_dma_map_sg(struct device *dev, struct scatterlist *sg,
|
||||
int nents, int mapped_ents, int direction)
|
||||
@@ -1310,7 +1307,7 @@ void debug_dma_map_sg(struct device *dev, struct scatterlist *sg,
|
||||
entry->type = dma_debug_sg;
|
||||
entry->dev = dev;
|
||||
entry->pfn = page_to_pfn(sg_page(s));
|
||||
entry->offset = s->offset,
|
||||
entry->offset = s->offset;
|
||||
entry->size = sg_dma_len(s);
|
||||
entry->dev_addr = sg_dma_address(s);
|
||||
entry->direction = direction;
|
||||
@@ -1328,7 +1325,6 @@ void debug_dma_map_sg(struct device *dev, struct scatterlist *sg,
|
||||
add_dma_entry(entry);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(debug_dma_map_sg);
|
||||
|
||||
static int get_nr_mapped_entries(struct device *dev,
|
||||
struct dma_debug_entry *ref)
|
||||
@@ -1380,7 +1376,6 @@ void debug_dma_unmap_sg(struct device *dev, struct scatterlist *sglist,
|
||||
check_unmap(&ref);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(debug_dma_unmap_sg);
|
||||
|
||||
void debug_dma_alloc_coherent(struct device *dev, size_t size,
|
||||
dma_addr_t dma_addr, void *virt)
|
||||
@@ -1466,7 +1461,6 @@ void debug_dma_map_resource(struct device *dev, phys_addr_t addr, size_t size,
|
||||
|
||||
add_dma_entry(entry);
|
||||
}
|
||||
EXPORT_SYMBOL(debug_dma_map_resource);
|
||||
|
||||
void debug_dma_unmap_resource(struct device *dev, dma_addr_t dma_addr,
|
||||
size_t size, int direction)
|
||||
@@ -1484,7 +1478,6 @@ void debug_dma_unmap_resource(struct device *dev, dma_addr_t dma_addr,
|
||||
|
||||
check_unmap(&ref);
|
||||
}
|
||||
EXPORT_SYMBOL(debug_dma_unmap_resource);
|
||||
|
||||
void debug_dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle,
|
||||
size_t size, int direction)
|
||||
@@ -1503,7 +1496,6 @@ void debug_dma_sync_single_for_cpu(struct device *dev, dma_addr_t dma_handle,
|
||||
|
||||
check_sync(dev, &ref, true);
|
||||
}
|
||||
EXPORT_SYMBOL(debug_dma_sync_single_for_cpu);
|
||||
|
||||
void debug_dma_sync_single_for_device(struct device *dev,
|
||||
dma_addr_t dma_handle, size_t size,
|
||||
@@ -1523,7 +1515,6 @@ void debug_dma_sync_single_for_device(struct device *dev,
|
||||
|
||||
check_sync(dev, &ref, false);
|
||||
}
|
||||
EXPORT_SYMBOL(debug_dma_sync_single_for_device);
|
||||
|
||||
void debug_dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg,
|
||||
int nelems, int direction)
|
||||
@@ -1556,7 +1547,6 @@ void debug_dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sg,
|
||||
check_sync(dev, &ref, true);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(debug_dma_sync_sg_for_cpu);
|
||||
|
||||
void debug_dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg,
|
||||
int nelems, int direction)
|
||||
@@ -1588,7 +1578,6 @@ void debug_dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg,
|
||||
check_sync(dev, &ref, false);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(debug_dma_sync_sg_for_device);
|
||||
|
||||
static int __init dma_debug_driver_setup(char *str)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (C) 2008 Advanced Micro Devices, Inc.
|
||||
*
|
||||
* Author: Joerg Roedel <joerg.roedel@amd.com>
|
||||
*/
|
||||
|
||||
#ifndef _KERNEL_DMA_DEBUG_H
|
||||
#define _KERNEL_DMA_DEBUG_H
|
||||
|
||||
#ifdef CONFIG_DMA_API_DEBUG
|
||||
extern void debug_dma_map_page(struct device *dev, struct page *page,
|
||||
size_t offset, size_t size,
|
||||
int direction, dma_addr_t dma_addr);
|
||||
|
||||
extern void debug_dma_unmap_page(struct device *dev, dma_addr_t addr,
|
||||
size_t size, int direction);
|
||||
|
||||
extern void debug_dma_map_sg(struct device *dev, struct scatterlist *sg,
|
||||
int nents, int mapped_ents, int direction);
|
||||
|
||||
extern void debug_dma_unmap_sg(struct device *dev, struct scatterlist *sglist,
|
||||
int nelems, int dir);
|
||||
|
||||
extern void debug_dma_alloc_coherent(struct device *dev, size_t size,
|
||||
dma_addr_t dma_addr, void *virt);
|
||||
|
||||
extern void debug_dma_free_coherent(struct device *dev, size_t size,
|
||||
void *virt, dma_addr_t addr);
|
||||
|
||||
extern void debug_dma_map_resource(struct device *dev, phys_addr_t addr,
|
||||
size_t size, int direction,
|
||||
dma_addr_t dma_addr);
|
||||
|
||||
extern void debug_dma_unmap_resource(struct device *dev, dma_addr_t dma_addr,
|
||||
size_t size, int direction);
|
||||
|
||||
extern void debug_dma_sync_single_for_cpu(struct device *dev,
|
||||
dma_addr_t dma_handle, size_t size,
|
||||
int direction);
|
||||
|
||||
extern void debug_dma_sync_single_for_device(struct device *dev,
|
||||
dma_addr_t dma_handle,
|
||||
size_t size, int direction);
|
||||
|
||||
extern void debug_dma_sync_sg_for_cpu(struct device *dev,
|
||||
struct scatterlist *sg,
|
||||
int nelems, int direction);
|
||||
|
||||
extern void debug_dma_sync_sg_for_device(struct device *dev,
|
||||
struct scatterlist *sg,
|
||||
int nelems, int direction);
|
||||
#else /* CONFIG_DMA_API_DEBUG */
|
||||
static inline void debug_dma_map_page(struct device *dev, struct page *page,
|
||||
size_t offset, size_t size,
|
||||
int direction, dma_addr_t dma_addr)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void debug_dma_unmap_page(struct device *dev, dma_addr_t addr,
|
||||
size_t size, int direction)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void debug_dma_map_sg(struct device *dev, struct scatterlist *sg,
|
||||
int nents, int mapped_ents, int direction)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void debug_dma_unmap_sg(struct device *dev,
|
||||
struct scatterlist *sglist,
|
||||
int nelems, int dir)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void debug_dma_alloc_coherent(struct device *dev, size_t size,
|
||||
dma_addr_t dma_addr, void *virt)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void debug_dma_free_coherent(struct device *dev, size_t size,
|
||||
void *virt, dma_addr_t addr)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void debug_dma_map_resource(struct device *dev, phys_addr_t addr,
|
||||
size_t size, int direction,
|
||||
dma_addr_t dma_addr)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void debug_dma_unmap_resource(struct device *dev,
|
||||
dma_addr_t dma_addr, size_t size,
|
||||
int direction)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void debug_dma_sync_single_for_cpu(struct device *dev,
|
||||
dma_addr_t dma_handle,
|
||||
size_t size, int direction)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void debug_dma_sync_single_for_device(struct device *dev,
|
||||
dma_addr_t dma_handle,
|
||||
size_t size, int direction)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void debug_dma_sync_sg_for_cpu(struct device *dev,
|
||||
struct scatterlist *sg,
|
||||
int nelems, int direction)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void debug_dma_sync_sg_for_device(struct device *dev,
|
||||
struct scatterlist *sg,
|
||||
int nelems, int direction)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_DMA_API_DEBUG */
|
||||
#endif /* _KERNEL_DMA_DEBUG_H */
|
||||
+173
-96
@@ -1,21 +1,22 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2018 Christoph Hellwig.
|
||||
* Copyright (C) 2018-2020 Christoph Hellwig.
|
||||
*
|
||||
* DMA operations that map physical memory directly without using an IOMMU.
|
||||
*/
|
||||
#include <linux/memblock.h> /* for max_pfn */
|
||||
#include <linux/export.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/dma-direct.h>
|
||||
#include <linux/dma-map-ops.h>
|
||||
#include <linux/scatterlist.h>
|
||||
#include <linux/dma-contiguous.h>
|
||||
#include <linux/pfn.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/set_memory.h>
|
||||
#include <linux/slab.h>
|
||||
#include "direct.h"
|
||||
|
||||
/*
|
||||
* Most architectures use ZONE_DMA for the first 16 Megabytes, but some use it
|
||||
* Most architectures use ZONE_DMA for the first 16 Megabytes, but some use
|
||||
* it for entirely different regions. In that case the arch code needs to
|
||||
* override the variable below for dma-direct to work properly.
|
||||
*/
|
||||
@@ -25,7 +26,7 @@ static inline dma_addr_t phys_to_dma_direct(struct device *dev,
|
||||
phys_addr_t phys)
|
||||
{
|
||||
if (force_dma_unencrypted(dev))
|
||||
return __phys_to_dma(dev, phys);
|
||||
return phys_to_dma_unencrypted(dev, phys);
|
||||
return phys_to_dma(dev, phys);
|
||||
}
|
||||
|
||||
@@ -43,16 +44,11 @@ u64 dma_direct_get_required_mask(struct device *dev)
|
||||
return (1ULL << (fls64(max_dma) - 1)) * 2 - 1;
|
||||
}
|
||||
|
||||
gfp_t dma_direct_optimal_gfp_mask(struct device *dev, u64 dma_mask,
|
||||
static gfp_t dma_direct_optimal_gfp_mask(struct device *dev, u64 dma_mask,
|
||||
u64 *phys_limit)
|
||||
{
|
||||
u64 dma_limit = min_not_zero(dma_mask, dev->bus_dma_limit);
|
||||
|
||||
if (force_dma_unencrypted(dev))
|
||||
*phys_limit = __dma_to_phys(dev, dma_limit);
|
||||
else
|
||||
*phys_limit = dma_to_phys(dev, dma_limit);
|
||||
|
||||
/*
|
||||
* Optimistically try the zone that the physical address mask falls
|
||||
* into first. If that returns memory that isn't actually addressable
|
||||
@@ -61,6 +57,7 @@ gfp_t dma_direct_optimal_gfp_mask(struct device *dev, u64 dma_mask,
|
||||
* Note that GFP_DMA32 and GFP_DMA are no ops without the corresponding
|
||||
* zones.
|
||||
*/
|
||||
*phys_limit = dma_to_phys(dev, dma_limit);
|
||||
if (*phys_limit <= DMA_BIT_MASK(zone_dma_bits))
|
||||
return GFP_DMA;
|
||||
if (*phys_limit <= DMA_BIT_MASK(32))
|
||||
@@ -68,47 +65,18 @@ gfp_t dma_direct_optimal_gfp_mask(struct device *dev, u64 dma_mask,
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool dma_coherent_ok(struct device *dev, phys_addr_t phys, size_t size)
|
||||
static bool dma_coherent_ok(struct device *dev, phys_addr_t phys, size_t size)
|
||||
{
|
||||
return phys_to_dma_direct(dev, phys) + size - 1 <=
|
||||
min_not_zero(dev->coherent_dma_mask, dev->bus_dma_limit);
|
||||
}
|
||||
dma_addr_t dma_addr = phys_to_dma_direct(dev, phys);
|
||||
|
||||
/*
|
||||
* Decrypting memory is allowed to block, so if this device requires
|
||||
* unencrypted memory it must come from atomic pools.
|
||||
*/
|
||||
static inline bool dma_should_alloc_from_pool(struct device *dev, gfp_t gfp,
|
||||
unsigned long attrs)
|
||||
{
|
||||
if (!IS_ENABLED(CONFIG_DMA_COHERENT_POOL))
|
||||
if (dma_addr == DMA_MAPPING_ERROR)
|
||||
return false;
|
||||
if (gfpflags_allow_blocking(gfp))
|
||||
return false;
|
||||
if (force_dma_unencrypted(dev))
|
||||
return true;
|
||||
if (!IS_ENABLED(CONFIG_DMA_DIRECT_REMAP))
|
||||
return false;
|
||||
if (dma_alloc_need_uncached(dev, attrs))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool dma_should_free_from_pool(struct device *dev,
|
||||
unsigned long attrs)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_DMA_COHERENT_POOL))
|
||||
return true;
|
||||
if ((attrs & DMA_ATTR_NO_KERNEL_MAPPING) &&
|
||||
!force_dma_unencrypted(dev))
|
||||
return false;
|
||||
if (IS_ENABLED(CONFIG_DMA_DIRECT_REMAP))
|
||||
return true;
|
||||
return false;
|
||||
return dma_addr + size - 1 <=
|
||||
min_not_zero(dev->coherent_dma_mask, dev->bus_dma_limit);
|
||||
}
|
||||
|
||||
static struct page *__dma_direct_alloc_pages(struct device *dev, size_t size,
|
||||
gfp_t gfp, unsigned long attrs)
|
||||
gfp_t gfp)
|
||||
{
|
||||
int node = dev_to_node(dev);
|
||||
struct page *page = NULL;
|
||||
@@ -116,11 +84,6 @@ static struct page *__dma_direct_alloc_pages(struct device *dev, size_t size,
|
||||
|
||||
WARN_ON_ONCE(!PAGE_ALIGNED(size));
|
||||
|
||||
if (attrs & DMA_ATTR_NO_WARN)
|
||||
gfp |= __GFP_NOWARN;
|
||||
|
||||
/* we always manually zero the memory once we are done: */
|
||||
gfp &= ~__GFP_ZERO;
|
||||
gfp |= dma_direct_optimal_gfp_mask(dev, dev->coherent_dma_mask,
|
||||
&phys_limit);
|
||||
page = dma_alloc_contiguous(dev, size, gfp);
|
||||
@@ -151,7 +114,23 @@ again:
|
||||
return page;
|
||||
}
|
||||
|
||||
void *dma_direct_alloc_pages(struct device *dev, size_t size,
|
||||
static void *dma_direct_alloc_from_pool(struct device *dev, size_t size,
|
||||
dma_addr_t *dma_handle, gfp_t gfp)
|
||||
{
|
||||
struct page *page;
|
||||
u64 phys_mask;
|
||||
void *ret;
|
||||
|
||||
gfp |= dma_direct_optimal_gfp_mask(dev, dev->coherent_dma_mask,
|
||||
&phys_mask);
|
||||
page = dma_alloc_from_pool(dev, size, &ret, gfp, dma_coherent_ok);
|
||||
if (!page)
|
||||
return NULL;
|
||||
*dma_handle = phys_to_dma_direct(dev, page_to_phys(page));
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *dma_direct_alloc(struct device *dev, size_t size,
|
||||
dma_addr_t *dma_handle, gfp_t gfp, unsigned long attrs)
|
||||
{
|
||||
struct page *page;
|
||||
@@ -159,30 +138,44 @@ void *dma_direct_alloc_pages(struct device *dev, size_t size,
|
||||
int err;
|
||||
|
||||
size = PAGE_ALIGN(size);
|
||||
|
||||
if (dma_should_alloc_from_pool(dev, gfp, attrs)) {
|
||||
ret = dma_alloc_from_pool(dev, size, &page, gfp);
|
||||
if (!ret)
|
||||
return NULL;
|
||||
goto done;
|
||||
}
|
||||
|
||||
page = __dma_direct_alloc_pages(dev, size, gfp, attrs);
|
||||
if (!page)
|
||||
return NULL;
|
||||
if (attrs & DMA_ATTR_NO_WARN)
|
||||
gfp |= __GFP_NOWARN;
|
||||
|
||||
if ((attrs & DMA_ATTR_NO_KERNEL_MAPPING) &&
|
||||
!force_dma_unencrypted(dev)) {
|
||||
page = __dma_direct_alloc_pages(dev, size, gfp & ~__GFP_ZERO);
|
||||
if (!page)
|
||||
return NULL;
|
||||
/* remove any dirty cache lines on the kernel alias */
|
||||
if (!PageHighMem(page))
|
||||
arch_dma_prep_coherent(page, size);
|
||||
*dma_handle = phys_to_dma_direct(dev, page_to_phys(page));
|
||||
/* return the page pointer as the opaque cookie */
|
||||
ret = page;
|
||||
goto done;
|
||||
return page;
|
||||
}
|
||||
|
||||
if (!IS_ENABLED(CONFIG_ARCH_HAS_DMA_SET_UNCACHED) &&
|
||||
!IS_ENABLED(CONFIG_DMA_DIRECT_REMAP) &&
|
||||
!dev_is_dma_coherent(dev))
|
||||
return arch_dma_alloc(dev, size, dma_handle, gfp, attrs);
|
||||
|
||||
/*
|
||||
* Remapping or decrypting memory may block. If either is required and
|
||||
* we can't block, allocate the memory from the atomic pools.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_DMA_COHERENT_POOL) &&
|
||||
!gfpflags_allow_blocking(gfp) &&
|
||||
(force_dma_unencrypted(dev) ||
|
||||
(IS_ENABLED(CONFIG_DMA_DIRECT_REMAP) && !dev_is_dma_coherent(dev))))
|
||||
return dma_direct_alloc_from_pool(dev, size, dma_handle, gfp);
|
||||
|
||||
/* we always manually zero the memory once we are done */
|
||||
page = __dma_direct_alloc_pages(dev, size, gfp & ~__GFP_ZERO);
|
||||
if (!page)
|
||||
return NULL;
|
||||
|
||||
if ((IS_ENABLED(CONFIG_DMA_DIRECT_REMAP) &&
|
||||
dma_alloc_need_uncached(dev, attrs)) ||
|
||||
!dev_is_dma_coherent(dev)) ||
|
||||
(IS_ENABLED(CONFIG_DMA_REMAP) && PageHighMem(page))) {
|
||||
/* remove any dirty cache lines on the kernel alias */
|
||||
arch_dma_prep_coherent(page, size);
|
||||
@@ -225,17 +218,14 @@ void *dma_direct_alloc_pages(struct device *dev, size_t size,
|
||||
memset(ret, 0, size);
|
||||
|
||||
if (IS_ENABLED(CONFIG_ARCH_HAS_DMA_SET_UNCACHED) &&
|
||||
dma_alloc_need_uncached(dev, attrs)) {
|
||||
!dev_is_dma_coherent(dev)) {
|
||||
arch_dma_prep_coherent(page, size);
|
||||
ret = arch_dma_set_uncached(ret, size);
|
||||
if (IS_ERR(ret))
|
||||
goto out_encrypt_pages;
|
||||
}
|
||||
done:
|
||||
if (force_dma_unencrypted(dev))
|
||||
*dma_handle = __phys_to_dma(dev, page_to_phys(page));
|
||||
else
|
||||
*dma_handle = phys_to_dma(dev, page_to_phys(page));
|
||||
*dma_handle = phys_to_dma_direct(dev, page_to_phys(page));
|
||||
return ret;
|
||||
|
||||
out_encrypt_pages:
|
||||
@@ -251,16 +241,11 @@ out_free_pages:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void dma_direct_free_pages(struct device *dev, size_t size, void *cpu_addr,
|
||||
dma_addr_t dma_addr, unsigned long attrs)
|
||||
void dma_direct_free(struct device *dev, size_t size,
|
||||
void *cpu_addr, dma_addr_t dma_addr, unsigned long attrs)
|
||||
{
|
||||
unsigned int page_order = get_order(size);
|
||||
|
||||
/* If cpu_addr is not from an atomic pool, dma_free_from_pool() fails */
|
||||
if (dma_should_free_from_pool(dev, attrs) &&
|
||||
dma_free_from_pool(dev, cpu_addr, PAGE_ALIGN(size)))
|
||||
return;
|
||||
|
||||
if ((attrs & DMA_ATTR_NO_KERNEL_MAPPING) &&
|
||||
!force_dma_unencrypted(dev)) {
|
||||
/* cpu_addr is a struct page cookie, not a kernel address */
|
||||
@@ -268,6 +253,18 @@ void dma_direct_free_pages(struct device *dev, size_t size, void *cpu_addr,
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IS_ENABLED(CONFIG_ARCH_HAS_DMA_SET_UNCACHED) &&
|
||||
!IS_ENABLED(CONFIG_DMA_DIRECT_REMAP) &&
|
||||
!dev_is_dma_coherent(dev)) {
|
||||
arch_dma_free(dev, size, cpu_addr, dma_addr, attrs);
|
||||
return;
|
||||
}
|
||||
|
||||
/* If cpu_addr is not from an atomic pool, dma_free_from_pool() fails */
|
||||
if (IS_ENABLED(CONFIG_DMA_COHERENT_POOL) &&
|
||||
dma_free_from_pool(dev, cpu_addr, PAGE_ALIGN(size)))
|
||||
return;
|
||||
|
||||
if (force_dma_unencrypted(dev))
|
||||
set_memory_encrypted((unsigned long)cpu_addr, 1 << page_order);
|
||||
|
||||
@@ -279,25 +276,60 @@ void dma_direct_free_pages(struct device *dev, size_t size, void *cpu_addr,
|
||||
dma_free_contiguous(dev, dma_direct_to_page(dev, dma_addr), size);
|
||||
}
|
||||
|
||||
void *dma_direct_alloc(struct device *dev, size_t size,
|
||||
dma_addr_t *dma_handle, gfp_t gfp, unsigned long attrs)
|
||||
struct page *dma_direct_alloc_pages(struct device *dev, size_t size,
|
||||
dma_addr_t *dma_handle, enum dma_data_direction dir, gfp_t gfp)
|
||||
{
|
||||
if (!IS_ENABLED(CONFIG_ARCH_HAS_DMA_SET_UNCACHED) &&
|
||||
!IS_ENABLED(CONFIG_DMA_DIRECT_REMAP) &&
|
||||
dma_alloc_need_uncached(dev, attrs))
|
||||
return arch_dma_alloc(dev, size, dma_handle, gfp, attrs);
|
||||
return dma_direct_alloc_pages(dev, size, dma_handle, gfp, attrs);
|
||||
struct page *page;
|
||||
void *ret;
|
||||
|
||||
if (IS_ENABLED(CONFIG_DMA_COHERENT_POOL) &&
|
||||
force_dma_unencrypted(dev) && !gfpflags_allow_blocking(gfp))
|
||||
return dma_direct_alloc_from_pool(dev, size, dma_handle, gfp);
|
||||
|
||||
page = __dma_direct_alloc_pages(dev, size, gfp);
|
||||
if (!page)
|
||||
return NULL;
|
||||
if (PageHighMem(page)) {
|
||||
/*
|
||||
* Depending on the cma= arguments and per-arch setup
|
||||
* dma_alloc_contiguous could return highmem pages.
|
||||
* Without remapping there is no way to return them here,
|
||||
* so log an error and fail.
|
||||
*/
|
||||
dev_info(dev, "Rejecting highmem page from CMA.\n");
|
||||
goto out_free_pages;
|
||||
}
|
||||
|
||||
ret = page_address(page);
|
||||
if (force_dma_unencrypted(dev)) {
|
||||
if (set_memory_decrypted((unsigned long)ret,
|
||||
1 << get_order(size)))
|
||||
goto out_free_pages;
|
||||
}
|
||||
memset(ret, 0, size);
|
||||
*dma_handle = phys_to_dma_direct(dev, page_to_phys(page));
|
||||
return page;
|
||||
out_free_pages:
|
||||
dma_free_contiguous(dev, page, size);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void dma_direct_free(struct device *dev, size_t size,
|
||||
void *cpu_addr, dma_addr_t dma_addr, unsigned long attrs)
|
||||
void dma_direct_free_pages(struct device *dev, size_t size,
|
||||
struct page *page, dma_addr_t dma_addr,
|
||||
enum dma_data_direction dir)
|
||||
{
|
||||
if (!IS_ENABLED(CONFIG_ARCH_HAS_DMA_SET_UNCACHED) &&
|
||||
!IS_ENABLED(CONFIG_DMA_DIRECT_REMAP) &&
|
||||
dma_alloc_need_uncached(dev, attrs))
|
||||
arch_dma_free(dev, size, cpu_addr, dma_addr, attrs);
|
||||
else
|
||||
dma_direct_free_pages(dev, size, cpu_addr, dma_addr, attrs);
|
||||
unsigned int page_order = get_order(size);
|
||||
void *vaddr = page_address(page);
|
||||
|
||||
/* If cpu_addr is not from an atomic pool, dma_free_from_pool() fails */
|
||||
if (IS_ENABLED(CONFIG_DMA_COHERENT_POOL) &&
|
||||
dma_free_from_pool(dev, vaddr, size))
|
||||
return;
|
||||
|
||||
if (force_dma_unencrypted(dev))
|
||||
set_memory_encrypted((unsigned long)vaddr, 1 << page_order);
|
||||
|
||||
dma_free_contiguous(dev, page, size);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \
|
||||
@@ -340,6 +372,9 @@ void dma_direct_sync_sg_for_cpu(struct device *dev,
|
||||
if (unlikely(is_swiotlb_buffer(paddr)))
|
||||
swiotlb_tbl_sync_single(dev, paddr, sg->length, dir,
|
||||
SYNC_FOR_CPU);
|
||||
|
||||
if (dir == DMA_FROM_DEVICE)
|
||||
arch_dma_mark_clean(paddr, sg->length);
|
||||
}
|
||||
|
||||
if (!dev_is_dma_coherent(dev))
|
||||
@@ -448,13 +483,13 @@ int dma_direct_supported(struct device *dev, u64 mask)
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* This check needs to be against the actual bit mask value, so
|
||||
* use __phys_to_dma() here so that the SME encryption mask isn't
|
||||
* This check needs to be against the actual bit mask value, so use
|
||||
* phys_to_dma_unencrypted() here so that the SME encryption mask isn't
|
||||
* part of the check.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_ZONE_DMA))
|
||||
min_mask = min_t(u64, min_mask, DMA_BIT_MASK(zone_dma_bits));
|
||||
return mask >= __phys_to_dma(dev, min_mask);
|
||||
return mask >= phys_to_dma_unencrypted(dev, min_mask);
|
||||
}
|
||||
|
||||
size_t dma_direct_max_mapping_size(struct device *dev)
|
||||
@@ -471,3 +506,45 @@ bool dma_direct_need_sync(struct device *dev, dma_addr_t dma_addr)
|
||||
return !dev_is_dma_coherent(dev) ||
|
||||
is_swiotlb_buffer(dma_to_phys(dev, dma_addr));
|
||||
}
|
||||
|
||||
/**
|
||||
* dma_direct_set_offset - Assign scalar offset for a single DMA range.
|
||||
* @dev: device pointer; needed to "own" the alloced memory.
|
||||
* @cpu_start: beginning of memory region covered by this offset.
|
||||
* @dma_start: beginning of DMA/PCI region covered by this offset.
|
||||
* @size: size of the region.
|
||||
*
|
||||
* This is for the simple case of a uniform offset which cannot
|
||||
* be discovered by "dma-ranges".
|
||||
*
|
||||
* It returns -ENOMEM if out of memory, -EINVAL if a map
|
||||
* already exists, 0 otherwise.
|
||||
*
|
||||
* Note: any call to this from a driver is a bug. The mapping needs
|
||||
* to be described by the device tree or other firmware interfaces.
|
||||
*/
|
||||
int dma_direct_set_offset(struct device *dev, phys_addr_t cpu_start,
|
||||
dma_addr_t dma_start, u64 size)
|
||||
{
|
||||
struct bus_dma_region *map;
|
||||
u64 offset = (u64)cpu_start - (u64)dma_start;
|
||||
|
||||
if (dev->dma_range_map) {
|
||||
dev_err(dev, "attempt to add DMA range to existing map\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!offset)
|
||||
return 0;
|
||||
|
||||
map = kcalloc(2, sizeof(*map), GFP_KERNEL);
|
||||
if (!map)
|
||||
return -ENOMEM;
|
||||
map[0].cpu_start = cpu_start;
|
||||
map[0].dma_start = dma_start;
|
||||
map[0].offset = offset;
|
||||
map[0].size = size;
|
||||
dev->dma_range_map = map;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dma_direct_set_offset);
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Copyright (C) 2018 Christoph Hellwig.
|
||||
*
|
||||
* DMA operations that map physical memory directly without using an IOMMU.
|
||||
*/
|
||||
#ifndef _KERNEL_DMA_DIRECT_H
|
||||
#define _KERNEL_DMA_DIRECT_H
|
||||
|
||||
#include <linux/dma-direct.h>
|
||||
|
||||
int dma_direct_get_sgtable(struct device *dev, struct sg_table *sgt,
|
||||
void *cpu_addr, dma_addr_t dma_addr, size_t size,
|
||||
unsigned long attrs);
|
||||
bool dma_direct_can_mmap(struct device *dev);
|
||||
int dma_direct_mmap(struct device *dev, struct vm_area_struct *vma,
|
||||
void *cpu_addr, dma_addr_t dma_addr, size_t size,
|
||||
unsigned long attrs);
|
||||
bool dma_direct_need_sync(struct device *dev, dma_addr_t dma_addr);
|
||||
int dma_direct_map_sg(struct device *dev, struct scatterlist *sgl, int nents,
|
||||
enum dma_data_direction dir, unsigned long attrs);
|
||||
size_t dma_direct_max_mapping_size(struct device *dev);
|
||||
|
||||
#if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \
|
||||
defined(CONFIG_SWIOTLB)
|
||||
void dma_direct_sync_sg_for_device(struct device *dev, struct scatterlist *sgl,
|
||||
int nents, enum dma_data_direction dir);
|
||||
#else
|
||||
static inline void dma_direct_sync_sg_for_device(struct device *dev,
|
||||
struct scatterlist *sgl, int nents, enum dma_data_direction dir)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) || \
|
||||
defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL) || \
|
||||
defined(CONFIG_SWIOTLB)
|
||||
void dma_direct_unmap_sg(struct device *dev, struct scatterlist *sgl,
|
||||
int nents, enum dma_data_direction dir, unsigned long attrs);
|
||||
void dma_direct_sync_sg_for_cpu(struct device *dev,
|
||||
struct scatterlist *sgl, int nents, enum dma_data_direction dir);
|
||||
#else
|
||||
static inline void dma_direct_unmap_sg(struct device *dev,
|
||||
struct scatterlist *sgl, int nents, enum dma_data_direction dir,
|
||||
unsigned long attrs)
|
||||
{
|
||||
}
|
||||
static inline void dma_direct_sync_sg_for_cpu(struct device *dev,
|
||||
struct scatterlist *sgl, int nents, enum dma_data_direction dir)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void dma_direct_sync_single_for_device(struct device *dev,
|
||||
dma_addr_t addr, size_t size, enum dma_data_direction dir)
|
||||
{
|
||||
phys_addr_t paddr = dma_to_phys(dev, addr);
|
||||
|
||||
if (unlikely(is_swiotlb_buffer(paddr)))
|
||||
swiotlb_tbl_sync_single(dev, paddr, size, dir, SYNC_FOR_DEVICE);
|
||||
|
||||
if (!dev_is_dma_coherent(dev))
|
||||
arch_sync_dma_for_device(paddr, size, dir);
|
||||
}
|
||||
|
||||
static inline void dma_direct_sync_single_for_cpu(struct device *dev,
|
||||
dma_addr_t addr, size_t size, enum dma_data_direction dir)
|
||||
{
|
||||
phys_addr_t paddr = dma_to_phys(dev, addr);
|
||||
|
||||
if (!dev_is_dma_coherent(dev)) {
|
||||
arch_sync_dma_for_cpu(paddr, size, dir);
|
||||
arch_sync_dma_for_cpu_all();
|
||||
}
|
||||
|
||||
if (unlikely(is_swiotlb_buffer(paddr)))
|
||||
swiotlb_tbl_sync_single(dev, paddr, size, dir, SYNC_FOR_CPU);
|
||||
|
||||
if (dir == DMA_FROM_DEVICE)
|
||||
arch_dma_mark_clean(paddr, size);
|
||||
}
|
||||
|
||||
static inline dma_addr_t dma_direct_map_page(struct device *dev,
|
||||
struct page *page, unsigned long offset, size_t size,
|
||||
enum dma_data_direction dir, unsigned long attrs)
|
||||
{
|
||||
phys_addr_t phys = page_to_phys(page) + offset;
|
||||
dma_addr_t dma_addr = phys_to_dma(dev, phys);
|
||||
|
||||
if (unlikely(swiotlb_force == SWIOTLB_FORCE))
|
||||
return swiotlb_map(dev, phys, size, dir, attrs);
|
||||
|
||||
if (unlikely(!dma_capable(dev, dma_addr, size, true))) {
|
||||
if (swiotlb_force != SWIOTLB_NO_FORCE)
|
||||
return swiotlb_map(dev, phys, size, dir, attrs);
|
||||
|
||||
dev_WARN_ONCE(dev, 1,
|
||||
"DMA addr %pad+%zu overflow (mask %llx, bus limit %llx).\n",
|
||||
&dma_addr, size, *dev->dma_mask, dev->bus_dma_limit);
|
||||
return DMA_MAPPING_ERROR;
|
||||
}
|
||||
|
||||
if (!dev_is_dma_coherent(dev) && !(attrs & DMA_ATTR_SKIP_CPU_SYNC))
|
||||
arch_sync_dma_for_device(phys, size, dir);
|
||||
return dma_addr;
|
||||
}
|
||||
|
||||
static inline void dma_direct_unmap_page(struct device *dev, dma_addr_t addr,
|
||||
size_t size, enum dma_data_direction dir, unsigned long attrs)
|
||||
{
|
||||
phys_addr_t phys = dma_to_phys(dev, addr);
|
||||
|
||||
if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC))
|
||||
dma_direct_sync_single_for_cpu(dev, addr, size, dir);
|
||||
|
||||
if (unlikely(is_swiotlb_buffer(phys)))
|
||||
swiotlb_tbl_unmap_single(dev, phys, size, size, dir, attrs);
|
||||
}
|
||||
#endif /* _KERNEL_DMA_DIRECT_H */
|
||||
+1
-2
@@ -2,7 +2,7 @@
|
||||
/*
|
||||
* Dummy DMA ops that always fail.
|
||||
*/
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dma-map-ops.h>
|
||||
|
||||
static int dma_dummy_mmap(struct device *dev, struct vm_area_struct *vma,
|
||||
void *cpu_addr, dma_addr_t dma_addr, size_t size,
|
||||
@@ -36,4 +36,3 @@ const struct dma_map_ops dma_dummy_ops = {
|
||||
.map_sg = dma_dummy_map_sg,
|
||||
.dma_supported = dma_dummy_supported,
|
||||
};
|
||||
EXPORT_SYMBOL(dma_dummy_ops);
|
||||
|
||||
+95
-64
@@ -7,13 +7,14 @@
|
||||
*/
|
||||
#include <linux/memblock.h> /* for max_pfn */
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/dma-direct.h>
|
||||
#include <linux/dma-noncoherent.h>
|
||||
#include <linux/dma-map-ops.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include "debug.h"
|
||||
#include "direct.h"
|
||||
|
||||
/*
|
||||
* Managed DMA API
|
||||
@@ -144,6 +145,10 @@ dma_addr_t dma_map_page_attrs(struct device *dev, struct page *page,
|
||||
dma_addr_t addr;
|
||||
|
||||
BUG_ON(!valid_dma_direction(dir));
|
||||
|
||||
if (WARN_ON_ONCE(!dev->dma_mask))
|
||||
return DMA_MAPPING_ERROR;
|
||||
|
||||
if (dma_map_direct(dev, ops))
|
||||
addr = dma_direct_map_page(dev, page, offset, size, dir, attrs);
|
||||
else
|
||||
@@ -179,6 +184,10 @@ int dma_map_sg_attrs(struct device *dev, struct scatterlist *sg, int nents,
|
||||
int ents;
|
||||
|
||||
BUG_ON(!valid_dma_direction(dir));
|
||||
|
||||
if (WARN_ON_ONCE(!dev->dma_mask))
|
||||
return 0;
|
||||
|
||||
if (dma_map_direct(dev, ops))
|
||||
ents = dma_direct_map_sg(dev, sg, nents, dir, attrs);
|
||||
else
|
||||
@@ -213,6 +222,9 @@ dma_addr_t dma_map_resource(struct device *dev, phys_addr_t phys_addr,
|
||||
|
||||
BUG_ON(!valid_dma_direction(dir));
|
||||
|
||||
if (WARN_ON_ONCE(!dev->dma_mask))
|
||||
return DMA_MAPPING_ERROR;
|
||||
|
||||
/* Don't allow RAM to be mapped */
|
||||
if (WARN_ON_ONCE(pfn_valid(PHYS_PFN(phys_addr))))
|
||||
return DMA_MAPPING_ERROR;
|
||||
@@ -295,22 +307,6 @@ void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg,
|
||||
}
|
||||
EXPORT_SYMBOL(dma_sync_sg_for_device);
|
||||
|
||||
/*
|
||||
* Create scatter-list for the already allocated DMA buffer.
|
||||
*/
|
||||
int dma_common_get_sgtable(struct device *dev, struct sg_table *sgt,
|
||||
void *cpu_addr, dma_addr_t dma_addr, size_t size,
|
||||
unsigned long attrs)
|
||||
{
|
||||
struct page *page = virt_to_page(cpu_addr);
|
||||
int ret;
|
||||
|
||||
ret = sg_alloc_table(sgt, 1, GFP_KERNEL);
|
||||
if (!ret)
|
||||
sg_set_page(sgt->sgl, page, PAGE_ALIGN(size), 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* The whole dma_get_sgtable() idea is fundamentally unsafe - it seems
|
||||
* that the intention is to allow exporting memory allocated via the
|
||||
@@ -346,9 +342,7 @@ pgprot_t dma_pgprot(struct device *dev, pgprot_t prot, unsigned long attrs)
|
||||
{
|
||||
if (force_dma_unencrypted(dev))
|
||||
prot = pgprot_decrypted(prot);
|
||||
if (dev_is_dma_coherent(dev) ||
|
||||
(IS_ENABLED(CONFIG_DMA_NONCOHERENT_CACHE_SYNC) &&
|
||||
(attrs & DMA_ATTR_NON_CONSISTENT)))
|
||||
if (dev_is_dma_coherent(dev))
|
||||
return prot;
|
||||
#ifdef CONFIG_ARCH_HAS_DMA_WRITE_COMBINE
|
||||
if (attrs & DMA_ATTR_WRITE_COMBINE)
|
||||
@@ -358,35 +352,6 @@ pgprot_t dma_pgprot(struct device *dev, pgprot_t prot, unsigned long attrs)
|
||||
}
|
||||
#endif /* CONFIG_MMU */
|
||||
|
||||
/*
|
||||
* Create userspace mapping for the DMA-coherent memory.
|
||||
*/
|
||||
int dma_common_mmap(struct device *dev, struct vm_area_struct *vma,
|
||||
void *cpu_addr, dma_addr_t dma_addr, size_t size,
|
||||
unsigned long attrs)
|
||||
{
|
||||
#ifdef CONFIG_MMU
|
||||
unsigned long user_count = vma_pages(vma);
|
||||
unsigned long count = PAGE_ALIGN(size) >> PAGE_SHIFT;
|
||||
unsigned long off = vma->vm_pgoff;
|
||||
int ret = -ENXIO;
|
||||
|
||||
vma->vm_page_prot = dma_pgprot(dev, vma->vm_page_prot, attrs);
|
||||
|
||||
if (dma_mmap_from_dev_coherent(dev, vma, cpu_addr, size, &ret))
|
||||
return ret;
|
||||
|
||||
if (off >= count || user_count > count - off)
|
||||
return -ENXIO;
|
||||
|
||||
return remap_pfn_range(vma, vma->vm_start,
|
||||
page_to_pfn(virt_to_page(cpu_addr)) + vma->vm_pgoff,
|
||||
user_count << PAGE_SHIFT, vma->vm_page_prot);
|
||||
#else
|
||||
return -ENXIO;
|
||||
#endif /* CONFIG_MMU */
|
||||
}
|
||||
|
||||
/**
|
||||
* dma_can_mmap - check if a given device supports dma_mmap_*
|
||||
* @dev: device to check
|
||||
@@ -506,6 +471,86 @@ void dma_free_attrs(struct device *dev, size_t size, void *cpu_addr,
|
||||
}
|
||||
EXPORT_SYMBOL(dma_free_attrs);
|
||||
|
||||
struct page *dma_alloc_pages(struct device *dev, size_t size,
|
||||
dma_addr_t *dma_handle, enum dma_data_direction dir, gfp_t gfp)
|
||||
{
|
||||
const struct dma_map_ops *ops = get_dma_ops(dev);
|
||||
struct page *page;
|
||||
|
||||
if (WARN_ON_ONCE(!dev->coherent_dma_mask))
|
||||
return NULL;
|
||||
if (WARN_ON_ONCE(gfp & (__GFP_DMA | __GFP_DMA32 | __GFP_HIGHMEM)))
|
||||
return NULL;
|
||||
|
||||
size = PAGE_ALIGN(size);
|
||||
if (dma_alloc_direct(dev, ops))
|
||||
page = dma_direct_alloc_pages(dev, size, dma_handle, dir, gfp);
|
||||
else if (ops->alloc_pages)
|
||||
page = ops->alloc_pages(dev, size, dma_handle, dir, gfp);
|
||||
else
|
||||
return NULL;
|
||||
|
||||
debug_dma_map_page(dev, page, 0, size, dir, *dma_handle);
|
||||
|
||||
return page;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dma_alloc_pages);
|
||||
|
||||
void dma_free_pages(struct device *dev, size_t size, struct page *page,
|
||||
dma_addr_t dma_handle, enum dma_data_direction dir)
|
||||
{
|
||||
const struct dma_map_ops *ops = get_dma_ops(dev);
|
||||
|
||||
size = PAGE_ALIGN(size);
|
||||
debug_dma_unmap_page(dev, dma_handle, size, dir);
|
||||
|
||||
if (dma_alloc_direct(dev, ops))
|
||||
dma_direct_free_pages(dev, size, page, dma_handle, dir);
|
||||
else if (ops->free_pages)
|
||||
ops->free_pages(dev, size, page, dma_handle, dir);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dma_free_pages);
|
||||
|
||||
void *dma_alloc_noncoherent(struct device *dev, size_t size,
|
||||
dma_addr_t *dma_handle, enum dma_data_direction dir, gfp_t gfp)
|
||||
{
|
||||
const struct dma_map_ops *ops = get_dma_ops(dev);
|
||||
void *vaddr;
|
||||
|
||||
if (!ops || !ops->alloc_noncoherent) {
|
||||
struct page *page;
|
||||
|
||||
page = dma_alloc_pages(dev, size, dma_handle, dir, gfp);
|
||||
if (!page)
|
||||
return NULL;
|
||||
return page_address(page);
|
||||
}
|
||||
|
||||
size = PAGE_ALIGN(size);
|
||||
vaddr = ops->alloc_noncoherent(dev, size, dma_handle, dir, gfp);
|
||||
if (vaddr)
|
||||
debug_dma_map_page(dev, virt_to_page(vaddr), 0, size, dir,
|
||||
*dma_handle);
|
||||
return vaddr;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dma_alloc_noncoherent);
|
||||
|
||||
void dma_free_noncoherent(struct device *dev, size_t size, void *vaddr,
|
||||
dma_addr_t dma_handle, enum dma_data_direction dir)
|
||||
{
|
||||
const struct dma_map_ops *ops = get_dma_ops(dev);
|
||||
|
||||
if (!ops || !ops->free_noncoherent) {
|
||||
dma_free_pages(dev, size, virt_to_page(vaddr), dma_handle, dir);
|
||||
return;
|
||||
}
|
||||
|
||||
size = PAGE_ALIGN(size);
|
||||
debug_dma_unmap_page(dev, dma_handle, size, dir);
|
||||
ops->free_noncoherent(dev, size, vaddr, dma_handle, dir);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dma_free_noncoherent);
|
||||
|
||||
int dma_supported(struct device *dev, u64 mask)
|
||||
{
|
||||
const struct dma_map_ops *ops = get_dma_ops(dev);
|
||||
@@ -563,20 +608,6 @@ int dma_set_coherent_mask(struct device *dev, u64 mask)
|
||||
EXPORT_SYMBOL(dma_set_coherent_mask);
|
||||
#endif
|
||||
|
||||
void dma_cache_sync(struct device *dev, void *vaddr, size_t size,
|
||||
enum dma_data_direction dir)
|
||||
{
|
||||
const struct dma_map_ops *ops = get_dma_ops(dev);
|
||||
|
||||
BUG_ON(!valid_dma_direction(dir));
|
||||
|
||||
if (dma_alloc_direct(dev, ops))
|
||||
arch_dma_cache_sync(dev, vaddr, size, dir);
|
||||
else if (ops->cache_sync)
|
||||
ops->cache_sync(dev, vaddr, size, dir);
|
||||
}
|
||||
EXPORT_SYMBOL(dma_cache_sync);
|
||||
|
||||
size_t dma_max_mapping_size(struct device *dev)
|
||||
{
|
||||
const struct dma_map_ops *ops = get_dma_ops(dev);
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Helpers for DMA ops implementations. These generally rely on the fact that
|
||||
* the allocated memory contains normal pages in the direct kernel mapping.
|
||||
*/
|
||||
#include <linux/dma-map-ops.h>
|
||||
|
||||
/*
|
||||
* Create scatter-list for the already allocated DMA buffer.
|
||||
*/
|
||||
int dma_common_get_sgtable(struct device *dev, struct sg_table *sgt,
|
||||
void *cpu_addr, dma_addr_t dma_addr, size_t size,
|
||||
unsigned long attrs)
|
||||
{
|
||||
struct page *page = virt_to_page(cpu_addr);
|
||||
int ret;
|
||||
|
||||
ret = sg_alloc_table(sgt, 1, GFP_KERNEL);
|
||||
if (!ret)
|
||||
sg_set_page(sgt->sgl, page, PAGE_ALIGN(size), 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create userspace mapping for the DMA-coherent memory.
|
||||
*/
|
||||
int dma_common_mmap(struct device *dev, struct vm_area_struct *vma,
|
||||
void *cpu_addr, dma_addr_t dma_addr, size_t size,
|
||||
unsigned long attrs)
|
||||
{
|
||||
#ifdef CONFIG_MMU
|
||||
unsigned long user_count = vma_pages(vma);
|
||||
unsigned long count = PAGE_ALIGN(size) >> PAGE_SHIFT;
|
||||
unsigned long off = vma->vm_pgoff;
|
||||
int ret = -ENXIO;
|
||||
|
||||
vma->vm_page_prot = dma_pgprot(dev, vma->vm_page_prot, attrs);
|
||||
|
||||
if (dma_mmap_from_dev_coherent(dev, vma, cpu_addr, size, &ret))
|
||||
return ret;
|
||||
|
||||
if (off >= count || user_count > count - off)
|
||||
return -ENXIO;
|
||||
|
||||
return remap_pfn_range(vma, vma->vm_start,
|
||||
page_to_pfn(virt_to_page(cpu_addr)) + vma->vm_pgoff,
|
||||
user_count << PAGE_SHIFT, vma->vm_page_prot);
|
||||
#else
|
||||
return -ENXIO;
|
||||
#endif /* CONFIG_MMU */
|
||||
}
|
||||
|
||||
struct page *dma_common_alloc_pages(struct device *dev, size_t size,
|
||||
dma_addr_t *dma_handle, enum dma_data_direction dir, gfp_t gfp)
|
||||
{
|
||||
const struct dma_map_ops *ops = get_dma_ops(dev);
|
||||
struct page *page;
|
||||
|
||||
page = dma_alloc_contiguous(dev, size, gfp);
|
||||
if (!page)
|
||||
page = alloc_pages_node(dev_to_node(dev), gfp, get_order(size));
|
||||
if (!page)
|
||||
return NULL;
|
||||
|
||||
*dma_handle = ops->map_page(dev, page, 0, size, dir,
|
||||
DMA_ATTR_SKIP_CPU_SYNC);
|
||||
if (*dma_handle == DMA_MAPPING_ERROR) {
|
||||
dma_free_contiguous(dev, page, size);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memset(page_address(page), 0, size);
|
||||
return page;
|
||||
}
|
||||
|
||||
void dma_common_free_pages(struct device *dev, size_t size, struct page *page,
|
||||
dma_addr_t dma_handle, enum dma_data_direction dir)
|
||||
{
|
||||
const struct dma_map_ops *ops = get_dma_ops(dev);
|
||||
|
||||
if (ops->unmap_page)
|
||||
ops->unmap_page(dev, dma_handle, size, dir,
|
||||
DMA_ATTR_SKIP_CPU_SYNC);
|
||||
dma_free_contiguous(dev, page, size);
|
||||
}
|
||||
+81
-71
@@ -3,9 +3,10 @@
|
||||
* Copyright (C) 2012 ARM Ltd.
|
||||
* Copyright (C) 2020 Google LLC
|
||||
*/
|
||||
#include <linux/cma.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/dma-map-ops.h>
|
||||
#include <linux/dma-direct.h>
|
||||
#include <linux/dma-noncoherent.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/genalloc.h>
|
||||
#include <linux/set_memory.h>
|
||||
@@ -55,11 +56,34 @@ static void dma_atomic_pool_size_add(gfp_t gfp, size_t size)
|
||||
pool_size_kernel += size;
|
||||
}
|
||||
|
||||
static bool cma_in_zone(gfp_t gfp)
|
||||
{
|
||||
unsigned long size;
|
||||
phys_addr_t end;
|
||||
struct cma *cma;
|
||||
|
||||
cma = dev_get_cma_area(NULL);
|
||||
if (!cma)
|
||||
return false;
|
||||
|
||||
size = cma_get_size(cma);
|
||||
if (!size)
|
||||
return false;
|
||||
|
||||
/* CMA can't cross zone boundaries, see cma_activate_area() */
|
||||
end = cma_get_base(cma) + size - 1;
|
||||
if (IS_ENABLED(CONFIG_ZONE_DMA) && (gfp & GFP_DMA))
|
||||
return end <= DMA_BIT_MASK(zone_dma_bits);
|
||||
if (IS_ENABLED(CONFIG_ZONE_DMA32) && (gfp & GFP_DMA32))
|
||||
return end <= DMA_BIT_MASK(32);
|
||||
return true;
|
||||
}
|
||||
|
||||
static int atomic_pool_expand(struct gen_pool *pool, size_t pool_size,
|
||||
gfp_t gfp)
|
||||
{
|
||||
unsigned int order;
|
||||
struct page *page;
|
||||
struct page *page = NULL;
|
||||
void *addr;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
@@ -68,7 +92,11 @@ static int atomic_pool_expand(struct gen_pool *pool, size_t pool_size,
|
||||
|
||||
do {
|
||||
pool_size = 1 << (PAGE_SHIFT + order);
|
||||
page = alloc_pages(gfp, order);
|
||||
if (cma_in_zone(gfp))
|
||||
page = dma_alloc_from_contiguous(NULL, 1 << order,
|
||||
order, false);
|
||||
if (!page)
|
||||
page = alloc_pages(gfp, order);
|
||||
} while (!page && order-- > 0);
|
||||
if (!page)
|
||||
goto out;
|
||||
@@ -86,7 +114,7 @@ static int atomic_pool_expand(struct gen_pool *pool, size_t pool_size,
|
||||
#endif
|
||||
/*
|
||||
* Memory in the atomic DMA pools must be unencrypted, the pools do not
|
||||
* shrink so no re-encryption occurs in dma_direct_free_pages().
|
||||
* shrink so no re-encryption occurs in dma_direct_free().
|
||||
*/
|
||||
ret = set_memory_decrypted((unsigned long)page_to_virt(page),
|
||||
1 << order);
|
||||
@@ -196,93 +224,75 @@ static int __init dma_atomic_pool_init(void)
|
||||
}
|
||||
postcore_initcall(dma_atomic_pool_init);
|
||||
|
||||
static inline struct gen_pool *dma_guess_pool_from_device(struct device *dev)
|
||||
static inline struct gen_pool *dma_guess_pool(struct gen_pool *prev, gfp_t gfp)
|
||||
{
|
||||
u64 phys_mask;
|
||||
gfp_t gfp;
|
||||
|
||||
gfp = dma_direct_optimal_gfp_mask(dev, dev->coherent_dma_mask,
|
||||
&phys_mask);
|
||||
if (IS_ENABLED(CONFIG_ZONE_DMA) && gfp == GFP_DMA)
|
||||
if (prev == NULL) {
|
||||
if (IS_ENABLED(CONFIG_ZONE_DMA32) && (gfp & GFP_DMA32))
|
||||
return atomic_pool_dma32;
|
||||
if (IS_ENABLED(CONFIG_ZONE_DMA) && (gfp & GFP_DMA))
|
||||
return atomic_pool_dma;
|
||||
return atomic_pool_kernel;
|
||||
}
|
||||
if (prev == atomic_pool_kernel)
|
||||
return atomic_pool_dma32 ? atomic_pool_dma32 : atomic_pool_dma;
|
||||
if (prev == atomic_pool_dma32)
|
||||
return atomic_pool_dma;
|
||||
if (IS_ENABLED(CONFIG_ZONE_DMA32) && gfp == GFP_DMA32)
|
||||
return atomic_pool_dma32;
|
||||
return atomic_pool_kernel;
|
||||
}
|
||||
|
||||
static inline struct gen_pool *dma_get_safer_pool(struct gen_pool *bad_pool)
|
||||
{
|
||||
if (bad_pool == atomic_pool_kernel)
|
||||
return atomic_pool_dma32 ? : atomic_pool_dma;
|
||||
|
||||
if (bad_pool == atomic_pool_dma32)
|
||||
return atomic_pool_dma;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline struct gen_pool *dma_guess_pool(struct device *dev,
|
||||
struct gen_pool *bad_pool)
|
||||
static struct page *__dma_alloc_from_pool(struct device *dev, size_t size,
|
||||
struct gen_pool *pool, void **cpu_addr,
|
||||
bool (*phys_addr_ok)(struct device *, phys_addr_t, size_t))
|
||||
{
|
||||
if (bad_pool)
|
||||
return dma_get_safer_pool(bad_pool);
|
||||
|
||||
return dma_guess_pool_from_device(dev);
|
||||
}
|
||||
|
||||
void *dma_alloc_from_pool(struct device *dev, size_t size,
|
||||
struct page **ret_page, gfp_t flags)
|
||||
{
|
||||
struct gen_pool *pool = NULL;
|
||||
unsigned long val = 0;
|
||||
void *ptr = NULL;
|
||||
unsigned long addr;
|
||||
phys_addr_t phys;
|
||||
|
||||
while (1) {
|
||||
pool = dma_guess_pool(dev, pool);
|
||||
if (!pool) {
|
||||
WARN(1, "Failed to get suitable pool for %s\n",
|
||||
dev_name(dev));
|
||||
break;
|
||||
}
|
||||
addr = gen_pool_alloc(pool, size);
|
||||
if (!addr)
|
||||
return NULL;
|
||||
|
||||
val = gen_pool_alloc(pool, size);
|
||||
if (!val)
|
||||
continue;
|
||||
|
||||
phys = gen_pool_virt_to_phys(pool, val);
|
||||
if (dma_coherent_ok(dev, phys, size))
|
||||
break;
|
||||
|
||||
gen_pool_free(pool, val, size);
|
||||
val = 0;
|
||||
phys = gen_pool_virt_to_phys(pool, addr);
|
||||
if (phys_addr_ok && !phys_addr_ok(dev, phys, size)) {
|
||||
gen_pool_free(pool, addr, size);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (gen_pool_avail(pool) < atomic_pool_size)
|
||||
schedule_work(&atomic_pool_work);
|
||||
|
||||
if (val) {
|
||||
*ret_page = pfn_to_page(__phys_to_pfn(phys));
|
||||
ptr = (void *)val;
|
||||
memset(ptr, 0, size);
|
||||
*cpu_addr = (void *)addr;
|
||||
memset(*cpu_addr, 0, size);
|
||||
return pfn_to_page(__phys_to_pfn(phys));
|
||||
}
|
||||
|
||||
if (gen_pool_avail(pool) < atomic_pool_size)
|
||||
schedule_work(&atomic_pool_work);
|
||||
struct page *dma_alloc_from_pool(struct device *dev, size_t size,
|
||||
void **cpu_addr, gfp_t gfp,
|
||||
bool (*phys_addr_ok)(struct device *, phys_addr_t, size_t))
|
||||
{
|
||||
struct gen_pool *pool = NULL;
|
||||
struct page *page;
|
||||
|
||||
while ((pool = dma_guess_pool(pool, gfp))) {
|
||||
page = __dma_alloc_from_pool(dev, size, pool, cpu_addr,
|
||||
phys_addr_ok);
|
||||
if (page)
|
||||
return page;
|
||||
}
|
||||
|
||||
return ptr;
|
||||
WARN(1, "Failed to get suitable pool for %s\n", dev_name(dev));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool dma_free_from_pool(struct device *dev, void *start, size_t size)
|
||||
{
|
||||
struct gen_pool *pool = NULL;
|
||||
|
||||
while (1) {
|
||||
pool = dma_guess_pool(dev, pool);
|
||||
if (!pool)
|
||||
return false;
|
||||
|
||||
if (gen_pool_has_addr(pool, (unsigned long)start, size)) {
|
||||
gen_pool_free(pool, (unsigned long)start, size);
|
||||
return true;
|
||||
}
|
||||
while ((pool = dma_guess_pool(pool, 0))) {
|
||||
if (!gen_pool_has_addr(pool, (unsigned long)start, size))
|
||||
continue;
|
||||
gen_pool_free(pool, (unsigned long)start, size);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
/*
|
||||
* Copyright (c) 2014 The Linux Foundation
|
||||
*/
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dma-map-ops.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
#include <linux/cache.h>
|
||||
#include <linux/dma-direct.h>
|
||||
#include <linux/dma-noncoherent.h>
|
||||
#include <linux/dma-map-ops.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/spinlock.h>
|
||||
@@ -93,7 +93,7 @@ static unsigned int io_tlb_index;
|
||||
* Max segment that we can provide which (if pages are contingous) will
|
||||
* not be bounced (unless SWIOTLB_FORCE is set).
|
||||
*/
|
||||
unsigned int max_segment;
|
||||
static unsigned int max_segment;
|
||||
|
||||
/*
|
||||
* We need to save away the original address corresponding to a mapped entry
|
||||
@@ -172,9 +172,7 @@ void swiotlb_print_info(void)
|
||||
return;
|
||||
}
|
||||
|
||||
pr_info("mapped [mem %#010llx-%#010llx] (%luMB)\n",
|
||||
(unsigned long long)io_tlb_start,
|
||||
(unsigned long long)io_tlb_end,
|
||||
pr_info("mapped [mem %pa-%pa] (%luMB)\n", &io_tlb_start, &io_tlb_end,
|
||||
bytes >> 20);
|
||||
}
|
||||
|
||||
@@ -670,13 +668,13 @@ dma_addr_t swiotlb_map(struct device *dev, phys_addr_t paddr, size_t size,
|
||||
swiotlb_force);
|
||||
|
||||
swiotlb_addr = swiotlb_tbl_map_single(dev,
|
||||
__phys_to_dma(dev, io_tlb_start),
|
||||
phys_to_dma_unencrypted(dev, io_tlb_start),
|
||||
paddr, size, size, dir, attrs);
|
||||
if (swiotlb_addr == (phys_addr_t)DMA_MAPPING_ERROR)
|
||||
return DMA_MAPPING_ERROR;
|
||||
|
||||
/* Ensure that the address returned is DMA'ble */
|
||||
dma_addr = __phys_to_dma(dev, swiotlb_addr);
|
||||
dma_addr = phys_to_dma_unencrypted(dev, swiotlb_addr);
|
||||
if (unlikely(!dma_capable(dev, dma_addr, size, true))) {
|
||||
swiotlb_tbl_unmap_single(dev, swiotlb_addr, size, size, dir,
|
||||
attrs | DMA_ATTR_SKIP_CPU_SYNC);
|
||||
|
||||
+3
-1
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
#include <linux/export.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dma-map-ops.h>
|
||||
#include <linux/scatterlist.h>
|
||||
|
||||
static void *dma_virt_alloc(struct device *dev, size_t size,
|
||||
@@ -55,5 +55,7 @@ const struct dma_map_ops dma_virt_ops = {
|
||||
.free = dma_virt_free,
|
||||
.map_page = dma_virt_map_page,
|
||||
.map_sg = dma_virt_map_sg,
|
||||
.alloc_pages = dma_common_alloc_pages,
|
||||
.free_pages = dma_common_free_pages,
|
||||
};
|
||||
EXPORT_SYMBOL(dma_virt_ops);
|
||||
|
||||
+36
-11
@@ -60,6 +60,9 @@ static long syscall_trace_enter(struct pt_regs *regs, long syscall,
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Either of the above might have changed the syscall number */
|
||||
syscall = syscall_get_nr(current, regs);
|
||||
|
||||
if (unlikely(ti_work & _TIF_SYSCALL_TRACEPOINT))
|
||||
trace_sys_enter(regs, syscall);
|
||||
|
||||
@@ -68,22 +71,45 @@ static long syscall_trace_enter(struct pt_regs *regs, long syscall,
|
||||
return ret ? : syscall;
|
||||
}
|
||||
|
||||
noinstr long syscall_enter_from_user_mode(struct pt_regs *regs, long syscall)
|
||||
static __always_inline long
|
||||
__syscall_enter_from_user_work(struct pt_regs *regs, long syscall)
|
||||
{
|
||||
unsigned long ti_work;
|
||||
|
||||
enter_from_user_mode(regs);
|
||||
instrumentation_begin();
|
||||
|
||||
local_irq_enable();
|
||||
ti_work = READ_ONCE(current_thread_info()->flags);
|
||||
if (ti_work & SYSCALL_ENTER_WORK)
|
||||
syscall = syscall_trace_enter(regs, syscall, ti_work);
|
||||
instrumentation_end();
|
||||
|
||||
return syscall;
|
||||
}
|
||||
|
||||
long syscall_enter_from_user_mode_work(struct pt_regs *regs, long syscall)
|
||||
{
|
||||
return __syscall_enter_from_user_work(regs, syscall);
|
||||
}
|
||||
|
||||
noinstr long syscall_enter_from_user_mode(struct pt_regs *regs, long syscall)
|
||||
{
|
||||
long ret;
|
||||
|
||||
enter_from_user_mode(regs);
|
||||
|
||||
instrumentation_begin();
|
||||
local_irq_enable();
|
||||
ret = __syscall_enter_from_user_work(regs, syscall);
|
||||
instrumentation_end();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
noinstr void syscall_enter_from_user_mode_prepare(struct pt_regs *regs)
|
||||
{
|
||||
enter_from_user_mode(regs);
|
||||
instrumentation_begin();
|
||||
local_irq_enable();
|
||||
instrumentation_end();
|
||||
}
|
||||
|
||||
/**
|
||||
* exit_to_user_mode - Fixup state when exiting to user mode
|
||||
*
|
||||
@@ -143,7 +169,6 @@ static unsigned long exit_to_user_mode_loop(struct pt_regs *regs,
|
||||
handle_signal_work(regs, ti_work);
|
||||
|
||||
if (ti_work & _TIF_NOTIFY_RESUME) {
|
||||
clear_thread_flag(TIF_NOTIFY_RESUME);
|
||||
tracehook_notify_resume(regs);
|
||||
rseq_handle_notify_resume(NULL, regs);
|
||||
}
|
||||
@@ -190,7 +215,7 @@ static inline bool report_single_step(unsigned long ti_work)
|
||||
/*
|
||||
* If TIF_SYSCALL_EMU is set, then the only reason to report is when
|
||||
* TIF_SINGLESTEP is set (i.e. PTRACE_SYSEMU_SINGLESTEP). This syscall
|
||||
* instruction has been already reported in syscall_enter_from_usermode().
|
||||
* instruction has been already reported in syscall_enter_from_user_mode().
|
||||
*/
|
||||
#define SYSEMU_STEP (_TIF_SINGLESTEP | _TIF_SYSCALL_EMU)
|
||||
|
||||
@@ -286,7 +311,7 @@ noinstr irqentry_state_t irqentry_enter(struct pt_regs *regs)
|
||||
* terminate a grace period, if and only if the timer interrupt is
|
||||
* not nested into another interrupt.
|
||||
*
|
||||
* Checking for __rcu_is_watching() here would prevent the nesting
|
||||
* Checking for rcu_is_watching() here would prevent the nesting
|
||||
* interrupt to invoke rcu_irq_enter(). If that nested interrupt is
|
||||
* the tick then rcu_flavor_sched_clock_irq() would wrongfully
|
||||
* assume that it is the first interupt and eventually claim
|
||||
@@ -320,10 +345,10 @@ noinstr irqentry_state_t irqentry_enter(struct pt_regs *regs)
|
||||
* already contains a warning when RCU is not watching, so no point
|
||||
* in having another one here.
|
||||
*/
|
||||
lockdep_hardirqs_off(CALLER_ADDR0);
|
||||
instrumentation_begin();
|
||||
rcu_irq_enter_check_tick();
|
||||
/* Use the combo lockdep/tracing function */
|
||||
trace_hardirqs_off();
|
||||
trace_hardirqs_off_finish();
|
||||
instrumentation_end();
|
||||
|
||||
return ret;
|
||||
|
||||
+1
-3
@@ -19,10 +19,8 @@ static int xfer_to_guest_mode_work(struct kvm_vcpu *vcpu, unsigned long ti_work)
|
||||
if (ti_work & _TIF_NEED_RESCHED)
|
||||
schedule();
|
||||
|
||||
if (ti_work & _TIF_NOTIFY_RESUME) {
|
||||
clear_thread_flag(TIF_NOTIFY_RESUME);
|
||||
if (ti_work & _TIF_NOTIFY_RESUME)
|
||||
tracehook_notify_resume(NULL);
|
||||
}
|
||||
|
||||
ret = arch_xfer_to_guest_mode_handle_work(vcpu, ti_work);
|
||||
if (ret)
|
||||
|
||||
+78
-50
@@ -99,7 +99,7 @@ static void remote_function(void *data)
|
||||
* retry due to any failures in smp_call_function_single(), such as if the
|
||||
* task_cpu() goes offline concurrently.
|
||||
*
|
||||
* returns @func return value or -ESRCH when the process isn't running
|
||||
* returns @func return value or -ESRCH or -ENXIO when the process isn't running
|
||||
*/
|
||||
static int
|
||||
task_function_call(struct task_struct *p, remote_function_f func, void *info)
|
||||
@@ -115,7 +115,8 @@ task_function_call(struct task_struct *p, remote_function_f func, void *info)
|
||||
for (;;) {
|
||||
ret = smp_call_function_single(task_cpu(p), remote_function,
|
||||
&data, 1);
|
||||
ret = !ret ? data.ret : -EAGAIN;
|
||||
if (!ret)
|
||||
ret = data.ret;
|
||||
|
||||
if (ret != -EAGAIN)
|
||||
break;
|
||||
@@ -382,7 +383,6 @@ static DEFINE_MUTEX(perf_sched_mutex);
|
||||
static atomic_t perf_sched_count;
|
||||
|
||||
static DEFINE_PER_CPU(atomic_t, perf_cgroup_events);
|
||||
static DEFINE_PER_CPU(int, perf_sched_cb_usages);
|
||||
static DEFINE_PER_CPU(struct pmu_event_list, pmu_sb_events);
|
||||
|
||||
static atomic_t nr_mmap_events __read_mostly;
|
||||
@@ -2133,8 +2133,24 @@ static inline struct list_head *get_event_list(struct perf_event *event)
|
||||
return event->attr.pinned ? &ctx->pinned_active : &ctx->flexible_active;
|
||||
}
|
||||
|
||||
/*
|
||||
* Events that have PERF_EV_CAP_SIBLING require being part of a group and
|
||||
* cannot exist on their own, schedule them out and move them into the ERROR
|
||||
* state. Also see _perf_event_enable(), it will not be able to recover
|
||||
* this ERROR state.
|
||||
*/
|
||||
static inline void perf_remove_sibling_event(struct perf_event *event)
|
||||
{
|
||||
struct perf_event_context *ctx = event->ctx;
|
||||
struct perf_cpu_context *cpuctx = __get_cpu_context(ctx);
|
||||
|
||||
event_sched_out(event, cpuctx, ctx);
|
||||
perf_event_set_state(event, PERF_EVENT_STATE_ERROR);
|
||||
}
|
||||
|
||||
static void perf_group_detach(struct perf_event *event)
|
||||
{
|
||||
struct perf_event *leader = event->group_leader;
|
||||
struct perf_event *sibling, *tmp;
|
||||
struct perf_event_context *ctx = event->ctx;
|
||||
|
||||
@@ -2153,7 +2169,7 @@ static void perf_group_detach(struct perf_event *event)
|
||||
/*
|
||||
* If this is a sibling, remove it from its group.
|
||||
*/
|
||||
if (event->group_leader != event) {
|
||||
if (leader != event) {
|
||||
list_del_init(&event->sibling_list);
|
||||
event->group_leader->nr_siblings--;
|
||||
goto out;
|
||||
@@ -2166,6 +2182,9 @@ static void perf_group_detach(struct perf_event *event)
|
||||
*/
|
||||
list_for_each_entry_safe(sibling, tmp, &event->sibling_list, sibling_list) {
|
||||
|
||||
if (sibling->event_caps & PERF_EV_CAP_SIBLING)
|
||||
perf_remove_sibling_event(sibling);
|
||||
|
||||
sibling->group_leader = sibling;
|
||||
list_del_init(&sibling->sibling_list);
|
||||
|
||||
@@ -2183,10 +2202,10 @@ static void perf_group_detach(struct perf_event *event)
|
||||
}
|
||||
|
||||
out:
|
||||
perf_event__header_size(event->group_leader);
|
||||
|
||||
for_each_sibling_event(tmp, event->group_leader)
|
||||
for_each_sibling_event(tmp, leader)
|
||||
perf_event__header_size(tmp);
|
||||
|
||||
perf_event__header_size(leader);
|
||||
}
|
||||
|
||||
static bool is_orphaned_event(struct perf_event *event)
|
||||
@@ -2979,6 +2998,7 @@ static void _perf_event_enable(struct perf_event *event)
|
||||
raw_spin_lock_irq(&ctx->lock);
|
||||
if (event->state >= PERF_EVENT_STATE_INACTIVE ||
|
||||
event->state < PERF_EVENT_STATE_ERROR) {
|
||||
out:
|
||||
raw_spin_unlock_irq(&ctx->lock);
|
||||
return;
|
||||
}
|
||||
@@ -2990,8 +3010,16 @@ static void _perf_event_enable(struct perf_event *event)
|
||||
* has gone back into error state, as distinct from the task having
|
||||
* been scheduled away before the cross-call arrived.
|
||||
*/
|
||||
if (event->state == PERF_EVENT_STATE_ERROR)
|
||||
if (event->state == PERF_EVENT_STATE_ERROR) {
|
||||
/*
|
||||
* Detached SIBLING events cannot leave ERROR state.
|
||||
*/
|
||||
if (event->event_caps & PERF_EV_CAP_SIBLING &&
|
||||
event->group_leader == event)
|
||||
goto out;
|
||||
|
||||
event->state = PERF_EVENT_STATE_OFF;
|
||||
}
|
||||
raw_spin_unlock_irq(&ctx->lock);
|
||||
|
||||
event_function_call(event, __perf_event_enable, NULL);
|
||||
@@ -3356,10 +3384,12 @@ static void perf_event_context_sched_out(struct task_struct *task, int ctxn,
|
||||
struct perf_event_context *parent, *next_parent;
|
||||
struct perf_cpu_context *cpuctx;
|
||||
int do_switch = 1;
|
||||
struct pmu *pmu;
|
||||
|
||||
if (likely(!ctx))
|
||||
return;
|
||||
|
||||
pmu = ctx->pmu;
|
||||
cpuctx = __get_cpu_context(ctx);
|
||||
if (!cpuctx->task_ctx)
|
||||
return;
|
||||
@@ -3389,11 +3419,15 @@ static void perf_event_context_sched_out(struct task_struct *task, int ctxn,
|
||||
raw_spin_lock(&ctx->lock);
|
||||
raw_spin_lock_nested(&next_ctx->lock, SINGLE_DEPTH_NESTING);
|
||||
if (context_equiv(ctx, next_ctx)) {
|
||||
struct pmu *pmu = ctx->pmu;
|
||||
|
||||
WRITE_ONCE(ctx->task, next);
|
||||
WRITE_ONCE(next_ctx->task, task);
|
||||
|
||||
perf_pmu_disable(pmu);
|
||||
|
||||
if (cpuctx->sched_cb_usage && pmu->sched_task)
|
||||
pmu->sched_task(ctx, false);
|
||||
|
||||
/*
|
||||
* PMU specific parts of task perf context can require
|
||||
* additional synchronization. As an example of such
|
||||
@@ -3405,6 +3439,8 @@ static void perf_event_context_sched_out(struct task_struct *task, int ctxn,
|
||||
else
|
||||
swap(ctx->task_ctx_data, next_ctx->task_ctx_data);
|
||||
|
||||
perf_pmu_enable(pmu);
|
||||
|
||||
/*
|
||||
* RCU_INIT_POINTER here is safe because we've not
|
||||
* modified the ctx and the above modification of
|
||||
@@ -3427,21 +3463,22 @@ unlock:
|
||||
|
||||
if (do_switch) {
|
||||
raw_spin_lock(&ctx->lock);
|
||||
perf_pmu_disable(pmu);
|
||||
|
||||
if (cpuctx->sched_cb_usage && pmu->sched_task)
|
||||
pmu->sched_task(ctx, false);
|
||||
task_ctx_sched_out(cpuctx, ctx, EVENT_ALL);
|
||||
|
||||
perf_pmu_enable(pmu);
|
||||
raw_spin_unlock(&ctx->lock);
|
||||
}
|
||||
}
|
||||
|
||||
static DEFINE_PER_CPU(struct list_head, sched_cb_list);
|
||||
|
||||
void perf_sched_cb_dec(struct pmu *pmu)
|
||||
{
|
||||
struct perf_cpu_context *cpuctx = this_cpu_ptr(pmu->pmu_cpu_context);
|
||||
|
||||
this_cpu_dec(perf_sched_cb_usages);
|
||||
|
||||
if (!--cpuctx->sched_cb_usage)
|
||||
list_del(&cpuctx->sched_cb_entry);
|
||||
--cpuctx->sched_cb_usage;
|
||||
}
|
||||
|
||||
|
||||
@@ -3449,10 +3486,7 @@ void perf_sched_cb_inc(struct pmu *pmu)
|
||||
{
|
||||
struct perf_cpu_context *cpuctx = this_cpu_ptr(pmu->pmu_cpu_context);
|
||||
|
||||
if (!cpuctx->sched_cb_usage++)
|
||||
list_add(&cpuctx->sched_cb_entry, this_cpu_ptr(&sched_cb_list));
|
||||
|
||||
this_cpu_inc(perf_sched_cb_usages);
|
||||
cpuctx->sched_cb_usage++;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -3463,30 +3497,22 @@ void perf_sched_cb_inc(struct pmu *pmu)
|
||||
* PEBS requires this to provide PID/TID information. This requires we flush
|
||||
* all queued PEBS records before we context switch to a new task.
|
||||
*/
|
||||
static void perf_pmu_sched_task(struct task_struct *prev,
|
||||
struct task_struct *next,
|
||||
bool sched_in)
|
||||
static void __perf_pmu_sched_task(struct perf_cpu_context *cpuctx, bool sched_in)
|
||||
{
|
||||
struct perf_cpu_context *cpuctx;
|
||||
struct pmu *pmu;
|
||||
|
||||
if (prev == next)
|
||||
pmu = cpuctx->ctx.pmu; /* software PMUs will not have sched_task */
|
||||
|
||||
if (WARN_ON_ONCE(!pmu->sched_task))
|
||||
return;
|
||||
|
||||
list_for_each_entry(cpuctx, this_cpu_ptr(&sched_cb_list), sched_cb_entry) {
|
||||
pmu = cpuctx->ctx.pmu; /* software PMUs will not have sched_task */
|
||||
perf_ctx_lock(cpuctx, cpuctx->task_ctx);
|
||||
perf_pmu_disable(pmu);
|
||||
|
||||
if (WARN_ON_ONCE(!pmu->sched_task))
|
||||
continue;
|
||||
pmu->sched_task(cpuctx->task_ctx, sched_in);
|
||||
|
||||
perf_ctx_lock(cpuctx, cpuctx->task_ctx);
|
||||
perf_pmu_disable(pmu);
|
||||
|
||||
pmu->sched_task(cpuctx->task_ctx, sched_in);
|
||||
|
||||
perf_pmu_enable(pmu);
|
||||
perf_ctx_unlock(cpuctx, cpuctx->task_ctx);
|
||||
}
|
||||
perf_pmu_enable(pmu);
|
||||
perf_ctx_unlock(cpuctx, cpuctx->task_ctx);
|
||||
}
|
||||
|
||||
static void perf_event_switch(struct task_struct *task,
|
||||
@@ -3511,9 +3537,6 @@ void __perf_event_task_sched_out(struct task_struct *task,
|
||||
{
|
||||
int ctxn;
|
||||
|
||||
if (__this_cpu_read(perf_sched_cb_usages))
|
||||
perf_pmu_sched_task(task, next, false);
|
||||
|
||||
if (atomic_read(&nr_switch_events))
|
||||
perf_event_switch(task, next, false);
|
||||
|
||||
@@ -3745,10 +3768,14 @@ static void perf_event_context_sched_in(struct perf_event_context *ctx,
|
||||
struct task_struct *task)
|
||||
{
|
||||
struct perf_cpu_context *cpuctx;
|
||||
struct pmu *pmu = ctx->pmu;
|
||||
|
||||
cpuctx = __get_cpu_context(ctx);
|
||||
if (cpuctx->task_ctx == ctx)
|
||||
if (cpuctx->task_ctx == ctx) {
|
||||
if (cpuctx->sched_cb_usage)
|
||||
__perf_pmu_sched_task(cpuctx, true);
|
||||
return;
|
||||
}
|
||||
|
||||
perf_ctx_lock(cpuctx, ctx);
|
||||
/*
|
||||
@@ -3758,7 +3785,7 @@ static void perf_event_context_sched_in(struct perf_event_context *ctx,
|
||||
if (!ctx->nr_events)
|
||||
goto unlock;
|
||||
|
||||
perf_pmu_disable(ctx->pmu);
|
||||
perf_pmu_disable(pmu);
|
||||
/*
|
||||
* We want to keep the following priority order:
|
||||
* cpu pinned (that don't need to move), task pinned,
|
||||
@@ -3770,7 +3797,11 @@ static void perf_event_context_sched_in(struct perf_event_context *ctx,
|
||||
if (!RB_EMPTY_ROOT(&ctx->pinned_groups.tree))
|
||||
cpu_ctx_sched_out(cpuctx, EVENT_FLEXIBLE);
|
||||
perf_event_sched_in(cpuctx, ctx, task);
|
||||
perf_pmu_enable(ctx->pmu);
|
||||
|
||||
if (cpuctx->sched_cb_usage && pmu->sched_task)
|
||||
pmu->sched_task(cpuctx->task_ctx, true);
|
||||
|
||||
perf_pmu_enable(pmu);
|
||||
|
||||
unlock:
|
||||
perf_ctx_unlock(cpuctx, ctx);
|
||||
@@ -3813,9 +3844,6 @@ void __perf_event_task_sched_in(struct task_struct *prev,
|
||||
|
||||
if (atomic_read(&nr_switch_events))
|
||||
perf_event_switch(task, prev, true);
|
||||
|
||||
if (__this_cpu_read(perf_sched_cb_usages))
|
||||
perf_pmu_sched_task(prev, task, true);
|
||||
}
|
||||
|
||||
static u64 perf_calculate_period(struct perf_event *event, u64 nsec, u64 count)
|
||||
@@ -5868,11 +5896,11 @@ static void perf_pmu_output_stop(struct perf_event *event);
|
||||
static void perf_mmap_close(struct vm_area_struct *vma)
|
||||
{
|
||||
struct perf_event *event = vma->vm_file->private_data;
|
||||
|
||||
struct perf_buffer *rb = ring_buffer_get(event);
|
||||
struct user_struct *mmap_user = rb->mmap_user;
|
||||
int mmap_locked = rb->mmap_locked;
|
||||
unsigned long size = perf_data_size(rb);
|
||||
bool detach_rest = false;
|
||||
|
||||
if (event->pmu->event_unmapped)
|
||||
event->pmu->event_unmapped(event, vma->vm_mm);
|
||||
@@ -5903,7 +5931,8 @@ static void perf_mmap_close(struct vm_area_struct *vma)
|
||||
mutex_unlock(&event->mmap_mutex);
|
||||
}
|
||||
|
||||
atomic_dec(&rb->mmap_count);
|
||||
if (atomic_dec_and_test(&rb->mmap_count))
|
||||
detach_rest = true;
|
||||
|
||||
if (!atomic_dec_and_mutex_lock(&event->mmap_count, &event->mmap_mutex))
|
||||
goto out_put;
|
||||
@@ -5912,7 +5941,7 @@ static void perf_mmap_close(struct vm_area_struct *vma)
|
||||
mutex_unlock(&event->mmap_mutex);
|
||||
|
||||
/* If there's still other mmap()s of this buffer, we're done. */
|
||||
if (atomic_read(&rb->mmap_count))
|
||||
if (!detach_rest)
|
||||
goto out_put;
|
||||
|
||||
/*
|
||||
@@ -10034,7 +10063,7 @@ perf_event_parse_addr_filter(struct perf_event *event, char *fstr,
|
||||
case IF_SRC_KERNELADDR:
|
||||
case IF_SRC_KERNEL:
|
||||
kernel = 1;
|
||||
/* fall through */
|
||||
fallthrough;
|
||||
|
||||
case IF_SRC_FILEADDR:
|
||||
case IF_SRC_FILE:
|
||||
@@ -12828,7 +12857,6 @@ static void __init perf_event_init_all_cpus(void)
|
||||
#ifdef CONFIG_CGROUP_PERF
|
||||
INIT_LIST_HEAD(&per_cpu(cgrp_cpuctx_list, cpu));
|
||||
#endif
|
||||
INIT_LIST_HEAD(&per_cpu(sched_cb_list, cpu));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -205,7 +205,7 @@ static int __replace_page(struct vm_area_struct *vma, unsigned long addr,
|
||||
try_to_free_swap(old_page);
|
||||
page_vma_mapped_walk_done(&pvmw);
|
||||
|
||||
if (vma->vm_flags & VM_LOCKED)
|
||||
if ((vma->vm_flags & VM_LOCKED) && !PageCompound(old_page))
|
||||
munlock_vma_page(old_page);
|
||||
put_page(old_page);
|
||||
|
||||
@@ -1823,7 +1823,7 @@ void uprobe_copy_process(struct task_struct *t, unsigned long flags)
|
||||
|
||||
t->utask->dup_xol_addr = area->vaddr;
|
||||
init_task_work(&t->utask->dup_xol_work, dup_xol_work);
|
||||
task_work_add(t, &t->utask->dup_xol_work, true);
|
||||
task_work_add(t, &t->utask->dup_xol_work, TWA_RESUME);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+8
-18
@@ -1474,23 +1474,6 @@ 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)
|
||||
{
|
||||
@@ -1498,6 +1481,7 @@ static long kernel_waitid(int which, pid_t upid, struct waitid_info *infop,
|
||||
struct pid *pid = NULL;
|
||||
enum pid_type type;
|
||||
long ret;
|
||||
unsigned int f_flags = 0;
|
||||
|
||||
if (options & ~(WNOHANG|WNOWAIT|WEXITED|WSTOPPED|WCONTINUED|
|
||||
__WNOTHREAD|__WCLONE|__WALL))
|
||||
@@ -1531,9 +1515,10 @@ static long kernel_waitid(int which, pid_t upid, struct waitid_info *infop,
|
||||
if (upid < 0)
|
||||
return -EINVAL;
|
||||
|
||||
pid = pidfd_get_pid(upid);
|
||||
pid = pidfd_get_pid(upid, &f_flags);
|
||||
if (IS_ERR(pid))
|
||||
return PTR_ERR(pid);
|
||||
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
@@ -1544,7 +1529,12 @@ static long kernel_waitid(int which, pid_t upid, struct waitid_info *infop,
|
||||
wo.wo_flags = options;
|
||||
wo.wo_info = infop;
|
||||
wo.wo_rusage = ru;
|
||||
if (f_flags & O_NONBLOCK)
|
||||
wo.wo_flags |= WNOHANG;
|
||||
|
||||
ret = do_wait(&wo);
|
||||
if (!ret && !(options & WNOHANG) && (f_flags & O_NONBLOCK))
|
||||
ret = -EAGAIN;
|
||||
|
||||
put_pid(pid);
|
||||
return ret;
|
||||
|
||||
+41
-13
@@ -95,6 +95,7 @@
|
||||
#include <linux/stackleak.h>
|
||||
#include <linux/kasan.h>
|
||||
#include <linux/scs.h>
|
||||
#include <linux/io_uring.h>
|
||||
|
||||
#include <asm/pgalloc.h>
|
||||
#include <linux/uaccess.h>
|
||||
@@ -555,10 +556,10 @@ static __latent_entropy int dup_mmap(struct mm_struct *mm,
|
||||
|
||||
get_file(file);
|
||||
if (tmp->vm_flags & VM_DENYWRITE)
|
||||
atomic_dec(&inode->i_writecount);
|
||||
put_write_access(inode);
|
||||
i_mmap_lock_write(mapping);
|
||||
if (tmp->vm_flags & VM_SHARED)
|
||||
atomic_inc(&mapping->i_mmap_writable);
|
||||
mapping_allow_writable(mapping);
|
||||
flush_dcache_mmap_lock(mapping);
|
||||
/* insert tmp into the share list, just after mpnt */
|
||||
vma_interval_tree_insert_after(tmp, mpnt,
|
||||
@@ -589,7 +590,7 @@ static __latent_entropy int dup_mmap(struct mm_struct *mm,
|
||||
|
||||
mm->map_count++;
|
||||
if (!(tmp->vm_flags & VM_WIPEONFORK))
|
||||
retval = copy_page_range(mm, oldmm, mpnt);
|
||||
retval = copy_page_range(tmp, mpnt);
|
||||
|
||||
if (tmp->vm_ops && tmp->vm_ops->open)
|
||||
tmp->vm_ops->open(tmp);
|
||||
@@ -728,6 +729,7 @@ void __put_task_struct(struct task_struct *tsk)
|
||||
WARN_ON(refcount_read(&tsk->usage));
|
||||
WARN_ON(tsk == current);
|
||||
|
||||
io_uring_free(tsk);
|
||||
cgroup_free(tsk);
|
||||
task_numa_free(tsk, true);
|
||||
security_task_free(tsk);
|
||||
@@ -1011,6 +1013,7 @@ static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p,
|
||||
mm_pgtables_bytes_init(mm);
|
||||
mm->map_count = 0;
|
||||
mm->locked_vm = 0;
|
||||
atomic_set(&mm->has_pinned, 0);
|
||||
atomic64_set(&mm->pinned_vm, 0);
|
||||
memset(&mm->rss_stat, 0, sizeof(mm->rss_stat));
|
||||
spin_lock_init(&mm->page_table_lock);
|
||||
@@ -1809,6 +1812,25 @@ static __always_inline void delayed_free_task(struct task_struct *tsk)
|
||||
free_task(tsk);
|
||||
}
|
||||
|
||||
static void copy_oom_score_adj(u64 clone_flags, struct task_struct *tsk)
|
||||
{
|
||||
/* Skip if kernel thread */
|
||||
if (!tsk->mm)
|
||||
return;
|
||||
|
||||
/* Skip if spawning a thread or using vfork */
|
||||
if ((clone_flags & (CLONE_VM | CLONE_THREAD | CLONE_VFORK)) != CLONE_VM)
|
||||
return;
|
||||
|
||||
/* We need to synchronize with __set_oom_adj */
|
||||
mutex_lock(&oom_adj_mutex);
|
||||
set_bit(MMF_MULTIPROCESS, &tsk->mm->flags);
|
||||
/* Update the values in case they were changed after copy_signal */
|
||||
tsk->signal->oom_score_adj = current->signal->oom_score_adj;
|
||||
tsk->signal->oom_score_adj_min = current->signal->oom_score_adj_min;
|
||||
mutex_unlock(&oom_adj_mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* This creates a new process as a copy of the old one,
|
||||
* but does not actually start it yet.
|
||||
@@ -1982,6 +2004,10 @@ static __latent_entropy struct task_struct *copy_process(
|
||||
p->vtime.state = VTIME_INACTIVE;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_IO_URING
|
||||
p->io_uring = NULL;
|
||||
#endif
|
||||
|
||||
#if defined(SPLIT_RSS_COUNTING)
|
||||
memset(&p->rss_stat, 0, sizeof(p->rss_stat));
|
||||
#endif
|
||||
@@ -2163,7 +2189,7 @@ static __latent_entropy struct task_struct *copy_process(
|
||||
|
||||
/*
|
||||
* Ensure that the cgroup subsystem policies allow the new process to be
|
||||
* forked. It should be noted the the new process's css_set can be changed
|
||||
* forked. It should be noted that the new process's css_set can be changed
|
||||
* between here and cgroup_post_fork() if an organisation operation is in
|
||||
* progress.
|
||||
*/
|
||||
@@ -2281,6 +2307,8 @@ static __latent_entropy struct task_struct *copy_process(
|
||||
trace_task_newtask(p, clone_flags);
|
||||
uprobe_copy_process(p, clone_flags);
|
||||
|
||||
copy_oom_score_adj(clone_flags, p);
|
||||
|
||||
return p;
|
||||
|
||||
bad_fork_cancel_cgroup:
|
||||
@@ -2384,14 +2412,14 @@ struct mm_struct *copy_init_mm(void)
|
||||
*
|
||||
* args->exit_signal is expected to be checked for sanity by the caller.
|
||||
*/
|
||||
long _do_fork(struct kernel_clone_args *args)
|
||||
pid_t kernel_clone(struct kernel_clone_args *args)
|
||||
{
|
||||
u64 clone_flags = args->flags;
|
||||
struct completion vfork;
|
||||
struct pid *pid;
|
||||
struct task_struct *p;
|
||||
int trace = 0;
|
||||
long nr;
|
||||
pid_t nr;
|
||||
|
||||
/*
|
||||
* For legacy clone() calls, CLONE_PIDFD uses the parent_tid argument
|
||||
@@ -2477,7 +2505,7 @@ pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
|
||||
.stack_size = (unsigned long)arg,
|
||||
};
|
||||
|
||||
return _do_fork(&args);
|
||||
return kernel_clone(&args);
|
||||
}
|
||||
|
||||
#ifdef __ARCH_WANT_SYS_FORK
|
||||
@@ -2488,7 +2516,7 @@ SYSCALL_DEFINE0(fork)
|
||||
.exit_signal = SIGCHLD,
|
||||
};
|
||||
|
||||
return _do_fork(&args);
|
||||
return kernel_clone(&args);
|
||||
#else
|
||||
/* can not support in nommu mode */
|
||||
return -EINVAL;
|
||||
@@ -2504,7 +2532,7 @@ SYSCALL_DEFINE0(vfork)
|
||||
.exit_signal = SIGCHLD,
|
||||
};
|
||||
|
||||
return _do_fork(&args);
|
||||
return kernel_clone(&args);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -2542,7 +2570,7 @@ SYSCALL_DEFINE5(clone, unsigned long, clone_flags, unsigned long, newsp,
|
||||
.tls = tls,
|
||||
};
|
||||
|
||||
return _do_fork(&args);
|
||||
return kernel_clone(&args);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -2700,7 +2728,7 @@ SYSCALL_DEFINE2(clone3, struct clone_args __user *, uargs, size_t, size)
|
||||
if (!clone3_args_valid(&kargs))
|
||||
return -EINVAL;
|
||||
|
||||
return _do_fork(&kargs);
|
||||
return kernel_clone(&kargs);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -2863,7 +2891,7 @@ int unshare_fd(unsigned long unshare_flags, unsigned int max_fds,
|
||||
/*
|
||||
* unshare allows a process to 'unshare' part of the process
|
||||
* context which was originally shared using clone. copy_*
|
||||
* functions used by _do_fork() cannot be used here directly
|
||||
* functions used by kernel_clone() cannot be used here directly
|
||||
* because they modify an inactive task_struct that is being
|
||||
* constructed. Here we are modifying the current, active,
|
||||
* task_struct.
|
||||
@@ -3014,7 +3042,7 @@ int unshare_files(struct files_struct **displaced)
|
||||
}
|
||||
|
||||
int sysctl_max_threads(struct ctl_table *table, int write,
|
||||
void __user *buffer, size_t *lenp, loff_t *ppos)
|
||||
void *buffer, size_t *lenp, loff_t *ppos)
|
||||
{
|
||||
struct ctl_table t;
|
||||
int ret;
|
||||
|
||||
+9
-2
@@ -39,6 +39,7 @@
|
||||
#include <linux/freezer.h>
|
||||
#include <linux/memblock.h>
|
||||
#include <linux/fault-inject.h>
|
||||
#include <linux/time_namespace.h>
|
||||
|
||||
#include <asm/futex.h>
|
||||
|
||||
@@ -916,7 +917,7 @@ static inline void exit_pi_state_list(struct task_struct *curr) { }
|
||||
* [10] Found | Found | task | !=taskTID | 0/1 | Invalid
|
||||
*
|
||||
* [1] Indicates that the kernel can acquire the futex atomically. We
|
||||
* came came here due to a stale FUTEX_WAITERS/FUTEX_OWNER_DIED bit.
|
||||
* came here due to a stale FUTEX_WAITERS/FUTEX_OWNER_DIED bit.
|
||||
*
|
||||
* [2] Valid, if TID does not belong to a kernel thread. If no matching
|
||||
* thread is found then it indicates that the owner TID has died.
|
||||
@@ -1502,8 +1503,10 @@ static int wake_futex_pi(u32 __user *uaddr, u32 uval, struct futex_pi_state *pi_
|
||||
*/
|
||||
newval = FUTEX_WAITERS | task_pid_vnr(new_owner);
|
||||
|
||||
if (unlikely(should_fail_futex(true)))
|
||||
if (unlikely(should_fail_futex(true))) {
|
||||
ret = -EFAULT;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
ret = cmpxchg_futex_value_locked(&curval, uaddr, uval, newval);
|
||||
if (!ret && (curval != uval)) {
|
||||
@@ -3797,6 +3800,8 @@ SYSCALL_DEFINE6(futex, u32 __user *, uaddr, int, op, u32, val,
|
||||
t = timespec64_to_ktime(ts);
|
||||
if (cmd == FUTEX_WAIT)
|
||||
t = ktime_add_safe(ktime_get(), t);
|
||||
else if (!(op & FUTEX_CLOCK_REALTIME))
|
||||
t = timens_ktime_to_host(CLOCK_MONOTONIC, t);
|
||||
tp = &t;
|
||||
}
|
||||
/*
|
||||
@@ -3989,6 +3994,8 @@ SYSCALL_DEFINE6(futex_time32, u32 __user *, uaddr, int, op, u32, val,
|
||||
t = timespec64_to_ktime(ts);
|
||||
if (cmd == FUTEX_WAIT)
|
||||
t = ktime_add_safe(ktime_get(), t);
|
||||
else if (!(op & FUTEX_CLOCK_REALTIME))
|
||||
t = timens_ktime_to_host(CLOCK_MONOTONIC, t);
|
||||
tp = &t;
|
||||
}
|
||||
if (cmd == FUTEX_REQUEUE || cmd == FUTEX_CMP_REQUEUE ||
|
||||
|
||||
@@ -19,7 +19,9 @@
|
||||
#include <linux/vmalloc.h>
|
||||
#include "gcov.h"
|
||||
|
||||
#if (__GNUC__ >= 7)
|
||||
#if (__GNUC__ >= 10)
|
||||
#define GCOV_COUNTERS 8
|
||||
#elif (__GNUC__ >= 7)
|
||||
#define GCOV_COUNTERS 9
|
||||
#elif (__GNUC__ > 5) || (__GNUC__ == 5 && __GNUC_MINOR__ >= 1)
|
||||
#define GCOV_COUNTERS 10
|
||||
|
||||
+1
-1
@@ -178,7 +178,7 @@ bool may_setgroups(void)
|
||||
{
|
||||
struct user_namespace *user_ns = current_user_ns();
|
||||
|
||||
return ns_capable(user_ns, CAP_SETGID) &&
|
||||
return ns_capable_setid(user_ns, CAP_SETGID) &&
|
||||
userns_may_setgroups(user_ns);
|
||||
}
|
||||
|
||||
|
||||
+31
-5
@@ -944,6 +944,33 @@ void handle_percpu_devid_irq(struct irq_desc *desc)
|
||||
chip->irq_eoi(&desc->irq_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* handle_percpu_devid_fasteoi_ipi - Per CPU local IPI handler with per cpu
|
||||
* dev ids
|
||||
* @desc: the interrupt description structure for this irq
|
||||
*
|
||||
* The biggest difference with the IRQ version is that the interrupt is
|
||||
* EOIed early, as the IPI could result in a context switch, and we need to
|
||||
* make sure the IPI can fire again. We also assume that the arch code has
|
||||
* registered an action. If not, we are positively doomed.
|
||||
*/
|
||||
void handle_percpu_devid_fasteoi_ipi(struct irq_desc *desc)
|
||||
{
|
||||
struct irq_chip *chip = irq_desc_get_chip(desc);
|
||||
struct irqaction *action = desc->action;
|
||||
unsigned int irq = irq_desc_get_irq(desc);
|
||||
irqreturn_t res;
|
||||
|
||||
__kstat_incr_irqs_this_cpu(desc);
|
||||
|
||||
if (chip->irq_eoi)
|
||||
chip->irq_eoi(&desc->irq_data);
|
||||
|
||||
trace_irq_handler_entry(irq, action);
|
||||
res = action->handler(irq, raw_cpu_ptr(action->percpu_dev_id));
|
||||
trace_irq_handler_exit(irq, action, res);
|
||||
}
|
||||
|
||||
/**
|
||||
* handle_percpu_devid_fasteoi_nmi - Per CPU local NMI handler with per cpu
|
||||
* dev ids
|
||||
@@ -1541,18 +1568,17 @@ EXPORT_SYMBOL_GPL(irq_chip_release_resources_parent);
|
||||
*/
|
||||
int irq_chip_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
|
||||
{
|
||||
struct irq_data *pos = NULL;
|
||||
struct irq_data *pos;
|
||||
|
||||
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
|
||||
for (; data; data = data->parent_data)
|
||||
#endif
|
||||
for (pos = NULL; !pos && data; data = irqd_get_parent_data(data)) {
|
||||
if (data->chip && data->chip->irq_compose_msi_msg)
|
||||
pos = data;
|
||||
}
|
||||
|
||||
if (!pos)
|
||||
return -ENOSYS;
|
||||
|
||||
pos->chip->irq_compose_msi_msg(pos, msg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@ static const struct irq_bit_descr irqchip_flags[] = {
|
||||
BIT_MASK_DESCR(IRQCHIP_EOI_THREADED),
|
||||
BIT_MASK_DESCR(IRQCHIP_SUPPORTS_LEVEL_MSI),
|
||||
BIT_MASK_DESCR(IRQCHIP_SUPPORTS_NMI),
|
||||
BIT_MASK_DESCR(IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND),
|
||||
};
|
||||
|
||||
static void
|
||||
@@ -125,6 +126,8 @@ static const struct irq_bit_descr irqdata_states[] = {
|
||||
BIT_MASK_DESCR(IRQD_DEFAULT_TRIGGER_SET),
|
||||
|
||||
BIT_MASK_DESCR(IRQD_HANDLE_ENFORCE_IRQCTX),
|
||||
|
||||
BIT_MASK_DESCR(IRQD_IRQ_ENABLED_ON_SUSPEND),
|
||||
};
|
||||
|
||||
static const struct irq_bit_descr irqdesc_states[] = {
|
||||
@@ -136,6 +139,7 @@ static const struct irq_bit_descr irqdesc_states[] = {
|
||||
BIT_MASK_DESCR(_IRQ_PER_CPU_DEVID),
|
||||
BIT_MASK_DESCR(_IRQ_IS_POLLED),
|
||||
BIT_MASK_DESCR(_IRQ_DISABLE_UNLAZY),
|
||||
BIT_MASK_DESCR(_IRQ_HIDDEN),
|
||||
};
|
||||
|
||||
static const struct irq_bit_descr irqdesc_istates[] = {
|
||||
|
||||
+1
-1
@@ -173,7 +173,7 @@ irqreturn_t __handle_irq_event_percpu(struct irq_desc *desc, unsigned int *flags
|
||||
|
||||
__irq_wake_thread(desc, action);
|
||||
|
||||
/* Fall through - to add to randomness */
|
||||
fallthrough; /* to add to randomness */
|
||||
case IRQ_HANDLED:
|
||||
*flags |= action->flags;
|
||||
break;
|
||||
|
||||
@@ -473,6 +473,15 @@ static inline void irq_domain_deactivate_irq(struct irq_data *data)
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline struct irq_data *irqd_get_parent_data(struct irq_data *irqd)
|
||||
{
|
||||
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
|
||||
return irqd->parent_data;
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef CONFIG_GENERIC_IRQ_DEBUGFS
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
|
||||
+95
-4
@@ -1136,6 +1136,17 @@ static struct irq_data *irq_domain_insert_irq_data(struct irq_domain *domain,
|
||||
return irq_data;
|
||||
}
|
||||
|
||||
static void __irq_domain_free_hierarchy(struct irq_data *irq_data)
|
||||
{
|
||||
struct irq_data *tmp;
|
||||
|
||||
while (irq_data) {
|
||||
tmp = irq_data;
|
||||
irq_data = irq_data->parent_data;
|
||||
kfree(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
static void irq_domain_free_irq_data(unsigned int virq, unsigned int nr_irqs)
|
||||
{
|
||||
struct irq_data *irq_data, *tmp;
|
||||
@@ -1147,12 +1158,83 @@ static void irq_domain_free_irq_data(unsigned int virq, unsigned int nr_irqs)
|
||||
irq_data->parent_data = NULL;
|
||||
irq_data->domain = NULL;
|
||||
|
||||
while (tmp) {
|
||||
irq_data = tmp;
|
||||
tmp = tmp->parent_data;
|
||||
kfree(irq_data);
|
||||
__irq_domain_free_hierarchy(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* irq_domain_disconnect_hierarchy - Mark the first unused level of a hierarchy
|
||||
* @domain: IRQ domain from which the hierarchy is to be disconnected
|
||||
* @virq: IRQ number where the hierarchy is to be trimmed
|
||||
*
|
||||
* Marks the @virq level belonging to @domain as disconnected.
|
||||
* Returns -EINVAL if @virq doesn't have a valid irq_data pointing
|
||||
* to @domain.
|
||||
*
|
||||
* Its only use is to be able to trim levels of hierarchy that do not
|
||||
* have any real meaning for this interrupt, and that the driver marks
|
||||
* as such from its .alloc() callback.
|
||||
*/
|
||||
int irq_domain_disconnect_hierarchy(struct irq_domain *domain,
|
||||
unsigned int virq)
|
||||
{
|
||||
struct irq_data *irqd;
|
||||
|
||||
irqd = irq_domain_get_irq_data(domain, virq);
|
||||
if (!irqd)
|
||||
return -EINVAL;
|
||||
|
||||
irqd->chip = ERR_PTR(-ENOTCONN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int irq_domain_trim_hierarchy(unsigned int virq)
|
||||
{
|
||||
struct irq_data *tail, *irqd, *irq_data;
|
||||
|
||||
irq_data = irq_get_irq_data(virq);
|
||||
tail = NULL;
|
||||
|
||||
/* The first entry must have a valid irqchip */
|
||||
if (!irq_data->chip || IS_ERR(irq_data->chip))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Validate that the irq_data chain is sane in the presence of
|
||||
* a hierarchy trimming marker.
|
||||
*/
|
||||
for (irqd = irq_data->parent_data; irqd; irq_data = irqd, irqd = irqd->parent_data) {
|
||||
/* Can't have a valid irqchip after a trim marker */
|
||||
if (irqd->chip && tail)
|
||||
return -EINVAL;
|
||||
|
||||
/* Can't have an empty irqchip before a trim marker */
|
||||
if (!irqd->chip && !tail)
|
||||
return -EINVAL;
|
||||
|
||||
if (IS_ERR(irqd->chip)) {
|
||||
/* Only -ENOTCONN is a valid trim marker */
|
||||
if (PTR_ERR(irqd->chip) != -ENOTCONN)
|
||||
return -EINVAL;
|
||||
|
||||
tail = irq_data;
|
||||
}
|
||||
}
|
||||
|
||||
/* No trim marker, nothing to do */
|
||||
if (!tail)
|
||||
return 0;
|
||||
|
||||
pr_info("IRQ%d: trimming hierarchy from %s\n",
|
||||
virq, tail->parent_data->domain->name);
|
||||
|
||||
/* Sever the inner part of the hierarchy... */
|
||||
irqd = tail;
|
||||
tail = tail->parent_data;
|
||||
irqd->parent_data = NULL;
|
||||
__irq_domain_free_hierarchy(tail);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int irq_domain_alloc_irq_data(struct irq_domain *domain,
|
||||
@@ -1362,6 +1444,15 @@ int __irq_domain_alloc_irqs(struct irq_domain *domain, int irq_base,
|
||||
mutex_unlock(&irq_domain_mutex);
|
||||
goto out_free_irq_data;
|
||||
}
|
||||
|
||||
for (i = 0; i < nr_irqs; i++) {
|
||||
ret = irq_domain_trim_hierarchy(virq + i);
|
||||
if (ret) {
|
||||
mutex_unlock(&irq_domain_mutex);
|
||||
goto out_free_irq_data;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < nr_irqs; i++)
|
||||
irq_domain_insert_irq(virq + i);
|
||||
mutex_unlock(&irq_domain_mutex);
|
||||
|
||||
+3
-3
@@ -271,7 +271,7 @@ int irq_do_set_affinity(struct irq_data *data, const struct cpumask *mask,
|
||||
case IRQ_SET_MASK_OK:
|
||||
case IRQ_SET_MASK_OK_DONE:
|
||||
cpumask_copy(desc->irq_common_data.affinity, mask);
|
||||
/* fall through */
|
||||
fallthrough;
|
||||
case IRQ_SET_MASK_OK_NOCOPY:
|
||||
irq_validate_effective_affinity(data);
|
||||
irq_set_thread_affinity(desc);
|
||||
@@ -868,7 +868,7 @@ int __irq_set_trigger(struct irq_desc *desc, unsigned long flags)
|
||||
case IRQ_SET_MASK_OK_DONE:
|
||||
irqd_clear(&desc->irq_data, IRQD_TRIGGER_MASK);
|
||||
irqd_set(&desc->irq_data, flags);
|
||||
/* fall through */
|
||||
fallthrough;
|
||||
|
||||
case IRQ_SET_MASK_OK_NOCOPY:
|
||||
flags = irqd_get_trigger_type(&desc->irq_data);
|
||||
@@ -1162,7 +1162,7 @@ static int irq_thread(void *data)
|
||||
handler_fn = irq_thread_fn;
|
||||
|
||||
init_task_work(&on_exit_work, irq_thread_dtor);
|
||||
task_work_add(current, &on_exit_work, false);
|
||||
task_work_add(current, &on_exit_work, TWA_NONE);
|
||||
|
||||
irq_thread_check_affinity(desc, action);
|
||||
|
||||
|
||||
@@ -380,6 +380,13 @@ int irq_matrix_alloc(struct irq_matrix *m, const struct cpumask *msk,
|
||||
unsigned int cpu, bit;
|
||||
struct cpumap *cm;
|
||||
|
||||
/*
|
||||
* Not required in theory, but matrix_find_best_cpu() uses
|
||||
* for_each_cpu() which ignores the cpumask on UP .
|
||||
*/
|
||||
if (cpumask_empty(msk))
|
||||
return -EINVAL;
|
||||
|
||||
cpu = matrix_find_best_cpu(m, msk);
|
||||
if (cpu == UINT_MAX)
|
||||
return -ENOSPC;
|
||||
|
||||
+54
-29
@@ -187,7 +187,6 @@ static const struct irq_domain_ops msi_domain_ops = {
|
||||
.deactivate = msi_domain_deactivate,
|
||||
};
|
||||
|
||||
#ifdef GENERIC_MSI_DOMAIN_OPS
|
||||
static irq_hw_number_t msi_domain_ops_get_hwirq(struct msi_domain_info *info,
|
||||
msi_alloc_info_t *arg)
|
||||
{
|
||||
@@ -206,11 +205,6 @@ static void msi_domain_ops_set_desc(msi_alloc_info_t *arg,
|
||||
{
|
||||
arg->desc = desc;
|
||||
}
|
||||
#else
|
||||
#define msi_domain_ops_get_hwirq NULL
|
||||
#define msi_domain_ops_prepare NULL
|
||||
#define msi_domain_ops_set_desc NULL
|
||||
#endif /* !GENERIC_MSI_DOMAIN_OPS */
|
||||
|
||||
static int msi_domain_ops_init(struct irq_domain *domain,
|
||||
struct msi_domain_info *info,
|
||||
@@ -235,11 +229,13 @@ static int msi_domain_ops_check(struct irq_domain *domain,
|
||||
}
|
||||
|
||||
static struct msi_domain_ops msi_domain_ops_default = {
|
||||
.get_hwirq = msi_domain_ops_get_hwirq,
|
||||
.msi_init = msi_domain_ops_init,
|
||||
.msi_check = msi_domain_ops_check,
|
||||
.msi_prepare = msi_domain_ops_prepare,
|
||||
.set_desc = msi_domain_ops_set_desc,
|
||||
.get_hwirq = msi_domain_ops_get_hwirq,
|
||||
.msi_init = msi_domain_ops_init,
|
||||
.msi_check = msi_domain_ops_check,
|
||||
.msi_prepare = msi_domain_ops_prepare,
|
||||
.set_desc = msi_domain_ops_set_desc,
|
||||
.domain_alloc_irqs = __msi_domain_alloc_irqs,
|
||||
.domain_free_irqs = __msi_domain_free_irqs,
|
||||
};
|
||||
|
||||
static void msi_domain_update_dom_ops(struct msi_domain_info *info)
|
||||
@@ -251,6 +247,14 @@ static void msi_domain_update_dom_ops(struct msi_domain_info *info)
|
||||
return;
|
||||
}
|
||||
|
||||
if (ops->domain_alloc_irqs == NULL)
|
||||
ops->domain_alloc_irqs = msi_domain_ops_default.domain_alloc_irqs;
|
||||
if (ops->domain_free_irqs == NULL)
|
||||
ops->domain_free_irqs = msi_domain_ops_default.domain_free_irqs;
|
||||
|
||||
if (!(info->flags & MSI_FLAG_USE_DEF_DOM_OPS))
|
||||
return;
|
||||
|
||||
if (ops->get_hwirq == NULL)
|
||||
ops->get_hwirq = msi_domain_ops_default.get_hwirq;
|
||||
if (ops->msi_init == NULL)
|
||||
@@ -284,8 +288,7 @@ struct irq_domain *msi_create_irq_domain(struct fwnode_handle *fwnode,
|
||||
{
|
||||
struct irq_domain *domain;
|
||||
|
||||
if (info->flags & MSI_FLAG_USE_DEF_DOM_OPS)
|
||||
msi_domain_update_dom_ops(info);
|
||||
msi_domain_update_dom_ops(info);
|
||||
if (info->flags & MSI_FLAG_USE_DEF_CHIP_OPS)
|
||||
msi_domain_update_chip_ops(info);
|
||||
|
||||
@@ -370,8 +373,13 @@ static bool msi_check_reservation_mode(struct irq_domain *domain,
|
||||
{
|
||||
struct msi_desc *desc;
|
||||
|
||||
if (domain->bus_token != DOMAIN_BUS_PCI_MSI)
|
||||
switch(domain->bus_token) {
|
||||
case DOMAIN_BUS_PCI_MSI:
|
||||
case DOMAIN_BUS_VMD_MSI:
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(info->flags & MSI_FLAG_MUST_REACTIVATE))
|
||||
return false;
|
||||
@@ -387,17 +395,8 @@ static bool msi_check_reservation_mode(struct irq_domain *domain,
|
||||
return desc->msi_attrib.is_msix || desc->msi_attrib.maskbit;
|
||||
}
|
||||
|
||||
/**
|
||||
* msi_domain_alloc_irqs - Allocate interrupts from a MSI interrupt domain
|
||||
* @domain: The domain to allocate from
|
||||
* @dev: Pointer to device struct of the device for which the interrupts
|
||||
* are allocated
|
||||
* @nvec: The number of interrupts to allocate
|
||||
*
|
||||
* Returns 0 on success or an error code.
|
||||
*/
|
||||
int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev,
|
||||
int nvec)
|
||||
int __msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev,
|
||||
int nvec)
|
||||
{
|
||||
struct msi_domain_info *info = domain->host_data;
|
||||
struct msi_domain_ops *ops = info->ops;
|
||||
@@ -491,12 +490,24 @@ cleanup:
|
||||
}
|
||||
|
||||
/**
|
||||
* msi_domain_free_irqs - Free interrupts from a MSI interrupt @domain associated tp @dev
|
||||
* @domain: The domain to managing the interrupts
|
||||
* msi_domain_alloc_irqs - Allocate interrupts from a MSI interrupt domain
|
||||
* @domain: The domain to allocate from
|
||||
* @dev: Pointer to device struct of the device for which the interrupts
|
||||
* are free
|
||||
* are allocated
|
||||
* @nvec: The number of interrupts to allocate
|
||||
*
|
||||
* Returns 0 on success or an error code.
|
||||
*/
|
||||
void msi_domain_free_irqs(struct irq_domain *domain, struct device *dev)
|
||||
int msi_domain_alloc_irqs(struct irq_domain *domain, struct device *dev,
|
||||
int nvec)
|
||||
{
|
||||
struct msi_domain_info *info = domain->host_data;
|
||||
struct msi_domain_ops *ops = info->ops;
|
||||
|
||||
return ops->domain_alloc_irqs(domain, dev, nvec);
|
||||
}
|
||||
|
||||
void __msi_domain_free_irqs(struct irq_domain *domain, struct device *dev)
|
||||
{
|
||||
struct msi_desc *desc;
|
||||
|
||||
@@ -513,6 +524,20 @@ void msi_domain_free_irqs(struct irq_domain *domain, struct device *dev)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* __msi_domain_free_irqs - Free interrupts from a MSI interrupt @domain associated tp @dev
|
||||
* @domain: The domain to managing the interrupts
|
||||
* @dev: Pointer to device struct of the device for which the interrupts
|
||||
* are free
|
||||
*/
|
||||
void msi_domain_free_irqs(struct irq_domain *domain, struct device *dev)
|
||||
{
|
||||
struct msi_domain_info *info = domain->host_data;
|
||||
struct msi_domain_ops *ops = info->ops;
|
||||
|
||||
return ops->domain_free_irqs(domain, dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* msi_get_domain_info - Get the MSI interrupt domain info for @domain
|
||||
* @domain: The interrupt domain to retrieve data from
|
||||
|
||||
+30
-4
@@ -69,12 +69,26 @@ void irq_pm_remove_action(struct irq_desc *desc, struct irqaction *action)
|
||||
|
||||
static bool suspend_device_irq(struct irq_desc *desc)
|
||||
{
|
||||
unsigned long chipflags = irq_desc_get_chip(desc)->flags;
|
||||
struct irq_data *irqd = &desc->irq_data;
|
||||
|
||||
if (!desc->action || irq_desc_is_chained(desc) ||
|
||||
desc->no_suspend_depth)
|
||||
return false;
|
||||
|
||||
if (irqd_is_wakeup_set(&desc->irq_data)) {
|
||||
irqd_set(&desc->irq_data, IRQD_WAKEUP_ARMED);
|
||||
if (irqd_is_wakeup_set(irqd)) {
|
||||
irqd_set(irqd, IRQD_WAKEUP_ARMED);
|
||||
|
||||
if ((chipflags & IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND) &&
|
||||
irqd_irq_disabled(irqd)) {
|
||||
/*
|
||||
* Interrupt marked for wakeup is in disabled state.
|
||||
* Enable interrupt here to unmask/enable in irqchip
|
||||
* to be able to resume with such interrupts.
|
||||
*/
|
||||
__enable_irq(desc);
|
||||
irqd_set(irqd, IRQD_IRQ_ENABLED_ON_SUSPEND);
|
||||
}
|
||||
/*
|
||||
* We return true here to force the caller to issue
|
||||
* synchronize_irq(). We need to make sure that the
|
||||
@@ -93,7 +107,7 @@ static bool suspend_device_irq(struct irq_desc *desc)
|
||||
* chip level. The chip implementation indicates that with
|
||||
* IRQCHIP_MASK_ON_SUSPEND.
|
||||
*/
|
||||
if (irq_desc_get_chip(desc)->flags & IRQCHIP_MASK_ON_SUSPEND)
|
||||
if (chipflags & IRQCHIP_MASK_ON_SUSPEND)
|
||||
mask_irq(desc);
|
||||
return true;
|
||||
}
|
||||
@@ -137,7 +151,19 @@ EXPORT_SYMBOL_GPL(suspend_device_irqs);
|
||||
|
||||
static void resume_irq(struct irq_desc *desc)
|
||||
{
|
||||
irqd_clear(&desc->irq_data, IRQD_WAKEUP_ARMED);
|
||||
struct irq_data *irqd = &desc->irq_data;
|
||||
|
||||
irqd_clear(irqd, IRQD_WAKEUP_ARMED);
|
||||
|
||||
if (irqd_is_enabled_on_suspend(irqd)) {
|
||||
/*
|
||||
* Interrupt marked for wakeup was enabled during suspend
|
||||
* entry. Disable such interrupts to restore them back to
|
||||
* original state.
|
||||
*/
|
||||
__disable_irq(desc);
|
||||
irqd_clear(irqd, IRQD_IRQ_ENABLED_ON_SUSPEND);
|
||||
}
|
||||
|
||||
if (desc->istate & IRQS_SUSPENDED)
|
||||
goto resume;
|
||||
|
||||
+1
-1
@@ -485,7 +485,7 @@ int show_interrupts(struct seq_file *p, void *v)
|
||||
|
||||
rcu_read_lock();
|
||||
desc = irq_to_desc(i);
|
||||
if (!desc)
|
||||
if (!desc || irq_settings_is_hidden(desc))
|
||||
goto outsparse;
|
||||
|
||||
if (desc->kstat_irqs)
|
||||
|
||||
+13
-2
@@ -86,6 +86,18 @@ static int irq_sw_resend(struct irq_desc *desc)
|
||||
}
|
||||
#endif
|
||||
|
||||
static int try_retrigger(struct irq_desc *desc)
|
||||
{
|
||||
if (desc->irq_data.chip->irq_retrigger)
|
||||
return desc->irq_data.chip->irq_retrigger(&desc->irq_data);
|
||||
|
||||
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY
|
||||
return irq_chip_retrigger_hierarchy(&desc->irq_data);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* IRQ resend
|
||||
*
|
||||
@@ -113,8 +125,7 @@ int check_irq_resend(struct irq_desc *desc, bool inject)
|
||||
|
||||
desc->istate &= ~IRQS_PENDING;
|
||||
|
||||
if (!desc->irq_data.chip->irq_retrigger ||
|
||||
!desc->irq_data.chip->irq_retrigger(&desc->irq_data))
|
||||
if (!try_retrigger(desc))
|
||||
err = irq_sw_resend(desc);
|
||||
|
||||
/* If the retrigger was successfull, mark it with the REPLAY bit */
|
||||
|
||||
@@ -17,6 +17,7 @@ enum {
|
||||
_IRQ_PER_CPU_DEVID = IRQ_PER_CPU_DEVID,
|
||||
_IRQ_IS_POLLED = IRQ_IS_POLLED,
|
||||
_IRQ_DISABLE_UNLAZY = IRQ_DISABLE_UNLAZY,
|
||||
_IRQ_HIDDEN = IRQ_HIDDEN,
|
||||
_IRQF_MODIFY_MASK = IRQF_MODIFY_MASK,
|
||||
};
|
||||
|
||||
@@ -31,6 +32,7 @@ enum {
|
||||
#define IRQ_PER_CPU_DEVID GOT_YOU_MORON
|
||||
#define IRQ_IS_POLLED GOT_YOU_MORON
|
||||
#define IRQ_DISABLE_UNLAZY GOT_YOU_MORON
|
||||
#define IRQ_HIDDEN GOT_YOU_MORON
|
||||
#undef IRQF_MODIFY_MASK
|
||||
#define IRQF_MODIFY_MASK GOT_YOU_MORON
|
||||
|
||||
@@ -167,3 +169,8 @@ static inline void irq_settings_clr_disable_unlazy(struct irq_desc *desc)
|
||||
{
|
||||
desc->status_use_accessors &= ~_IRQ_DISABLE_UNLAZY;
|
||||
}
|
||||
|
||||
static inline bool irq_settings_is_hidden(struct irq_desc *desc)
|
||||
{
|
||||
return desc->status_use_accessors & _IRQ_HIDDEN;
|
||||
}
|
||||
|
||||
@@ -604,7 +604,7 @@ int irq_timings_alloc(int irq)
|
||||
|
||||
/*
|
||||
* Some platforms can have the same private interrupt per cpu,
|
||||
* so this function may be be called several times with the
|
||||
* so this function may be called several times with the
|
||||
* same interrupt number. Just bail out in case the per cpu
|
||||
* stat structure is already allocated.
|
||||
*/
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user