mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-01-11 17:10:13 +00:00
Compare commits
19 Commits
40fbbd64bb
...
ea1013c153
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ea1013c153 | ||
|
|
64e68f8a95 | ||
|
|
489e96651d | ||
|
|
af241e6bfc | ||
|
|
4cb92fa763 | ||
|
|
b1aa01d312 | ||
|
|
1d528e794f | ||
|
|
79e247d660 | ||
|
|
ac44dcc788 | ||
|
|
9489d457d4 | ||
|
|
234483565d | ||
|
|
297c3fba9d | ||
|
|
01bc3b6db1 | ||
|
|
ca45c84afb | ||
|
|
189e5deb94 | ||
|
|
d70f79fef6 | ||
|
|
639f58a0f4 | ||
|
|
861111b698 | ||
|
|
835a507535 |
@ -748,6 +748,7 @@ ForEachMacros:
|
||||
- 'ynl_attr_for_each_nested'
|
||||
- 'ynl_attr_for_each_payload'
|
||||
- 'zorro_for_each_dev'
|
||||
- 'zpci_bus_for_each'
|
||||
|
||||
IncludeBlocks: Preserve
|
||||
IncludeCategories:
|
||||
|
||||
@ -1004,7 +1004,7 @@ static void __maybe_unused build_bhb_mitigation(struct jit_ctx *ctx)
|
||||
arm64_get_spectre_v2_state() == SPECTRE_VULNERABLE)
|
||||
return;
|
||||
|
||||
if (capable(CAP_SYS_ADMIN))
|
||||
if (ns_capable_noaudit(&init_user_ns, CAP_SYS_ADMIN))
|
||||
return;
|
||||
|
||||
if (supports_clearbhb(SCOPE_SYSTEM)) {
|
||||
|
||||
@ -15,6 +15,7 @@ struct ipl_pl_hdr {
|
||||
#define IPL_PL_FLAG_IPLPS 0x80
|
||||
#define IPL_PL_FLAG_SIPL 0x40
|
||||
#define IPL_PL_FLAG_IPLSR 0x20
|
||||
#define IPL_PL_FLAG_SBP 0x10
|
||||
|
||||
/* IPL Parameter Block header */
|
||||
struct ipl_pb_hdr {
|
||||
|
||||
@ -262,6 +262,24 @@ static struct kobj_attribute sys_##_prefix##_##_name##_attr = \
|
||||
sys_##_prefix##_##_name##_show, \
|
||||
sys_##_prefix##_##_name##_store)
|
||||
|
||||
#define DEFINE_IPL_ATTR_BOOTPROG_RW(_prefix, _name, _fmt_out, _fmt_in, _hdr, _value) \
|
||||
IPL_ATTR_SHOW_FN(_prefix, _name, _fmt_out, (unsigned long long) _value) \
|
||||
static ssize_t sys_##_prefix##_##_name##_store(struct kobject *kobj, \
|
||||
struct kobj_attribute *attr, \
|
||||
const char *buf, size_t len) \
|
||||
{ \
|
||||
unsigned long long value; \
|
||||
if (sscanf(buf, _fmt_in, &value) != 1) \
|
||||
return -EINVAL; \
|
||||
(_value) = value; \
|
||||
(_hdr).flags &= ~IPL_PL_FLAG_SBP; \
|
||||
return len; \
|
||||
} \
|
||||
static struct kobj_attribute sys_##_prefix##_##_name##_attr = \
|
||||
__ATTR(_name, 0644, \
|
||||
sys_##_prefix##_##_name##_show, \
|
||||
sys_##_prefix##_##_name##_store)
|
||||
|
||||
#define DEFINE_IPL_ATTR_STR_RW(_prefix, _name, _fmt_out, _fmt_in, _value)\
|
||||
IPL_ATTR_SHOW_FN(_prefix, _name, _fmt_out, _value) \
|
||||
static ssize_t sys_##_prefix##_##_name##_store(struct kobject *kobj, \
|
||||
@ -818,12 +836,13 @@ DEFINE_IPL_ATTR_RW(reipl_fcp, wwpn, "0x%016llx\n", "%llx\n",
|
||||
reipl_block_fcp->fcp.wwpn);
|
||||
DEFINE_IPL_ATTR_RW(reipl_fcp, lun, "0x%016llx\n", "%llx\n",
|
||||
reipl_block_fcp->fcp.lun);
|
||||
DEFINE_IPL_ATTR_RW(reipl_fcp, bootprog, "%lld\n", "%lld\n",
|
||||
reipl_block_fcp->fcp.bootprog);
|
||||
DEFINE_IPL_ATTR_RW(reipl_fcp, br_lba, "%lld\n", "%lld\n",
|
||||
reipl_block_fcp->fcp.br_lba);
|
||||
DEFINE_IPL_ATTR_RW(reipl_fcp, device, "0.0.%04llx\n", "0.0.%llx\n",
|
||||
reipl_block_fcp->fcp.devno);
|
||||
DEFINE_IPL_ATTR_BOOTPROG_RW(reipl_fcp, bootprog, "%lld\n", "%lld\n",
|
||||
reipl_block_fcp->hdr,
|
||||
reipl_block_fcp->fcp.bootprog);
|
||||
|
||||
static void reipl_get_ascii_loadparm(char *loadparm,
|
||||
struct ipl_parameter_block *ibp)
|
||||
@ -942,10 +961,11 @@ DEFINE_IPL_ATTR_RW(reipl_nvme, fid, "0x%08llx\n", "%llx\n",
|
||||
reipl_block_nvme->nvme.fid);
|
||||
DEFINE_IPL_ATTR_RW(reipl_nvme, nsid, "0x%08llx\n", "%llx\n",
|
||||
reipl_block_nvme->nvme.nsid);
|
||||
DEFINE_IPL_ATTR_RW(reipl_nvme, bootprog, "%lld\n", "%lld\n",
|
||||
reipl_block_nvme->nvme.bootprog);
|
||||
DEFINE_IPL_ATTR_RW(reipl_nvme, br_lba, "%lld\n", "%lld\n",
|
||||
reipl_block_nvme->nvme.br_lba);
|
||||
DEFINE_IPL_ATTR_BOOTPROG_RW(reipl_nvme, bootprog, "%lld\n", "%lld\n",
|
||||
reipl_block_nvme->hdr,
|
||||
reipl_block_nvme->nvme.bootprog);
|
||||
|
||||
static struct attribute *reipl_nvme_attrs[] = {
|
||||
&sys_reipl_nvme_fid_attr.attr,
|
||||
@ -1038,8 +1058,9 @@ static const struct bin_attribute *const reipl_eckd_bin_attrs[] = {
|
||||
};
|
||||
|
||||
DEFINE_IPL_CCW_ATTR_RW(reipl_eckd, device, reipl_block_eckd->eckd);
|
||||
DEFINE_IPL_ATTR_RW(reipl_eckd, bootprog, "%lld\n", "%lld\n",
|
||||
reipl_block_eckd->eckd.bootprog);
|
||||
DEFINE_IPL_ATTR_BOOTPROG_RW(reipl_eckd, bootprog, "%lld\n", "%lld\n",
|
||||
reipl_block_eckd->hdr,
|
||||
reipl_block_eckd->eckd.bootprog);
|
||||
|
||||
static struct attribute *reipl_eckd_attrs[] = {
|
||||
&sys_reipl_eckd_device_attr.attr,
|
||||
@ -1567,12 +1588,13 @@ DEFINE_IPL_ATTR_RW(dump_fcp, wwpn, "0x%016llx\n", "%llx\n",
|
||||
dump_block_fcp->fcp.wwpn);
|
||||
DEFINE_IPL_ATTR_RW(dump_fcp, lun, "0x%016llx\n", "%llx\n",
|
||||
dump_block_fcp->fcp.lun);
|
||||
DEFINE_IPL_ATTR_RW(dump_fcp, bootprog, "%lld\n", "%lld\n",
|
||||
dump_block_fcp->fcp.bootprog);
|
||||
DEFINE_IPL_ATTR_RW(dump_fcp, br_lba, "%lld\n", "%lld\n",
|
||||
dump_block_fcp->fcp.br_lba);
|
||||
DEFINE_IPL_ATTR_RW(dump_fcp, device, "0.0.%04llx\n", "0.0.%llx\n",
|
||||
dump_block_fcp->fcp.devno);
|
||||
DEFINE_IPL_ATTR_BOOTPROG_RW(dump_fcp, bootprog, "%lld\n", "%lld\n",
|
||||
dump_block_fcp->hdr,
|
||||
dump_block_fcp->fcp.bootprog);
|
||||
|
||||
DEFINE_IPL_ATTR_SCP_DATA_RW(dump_fcp, dump_block_fcp->hdr,
|
||||
dump_block_fcp->fcp,
|
||||
@ -1604,10 +1626,11 @@ DEFINE_IPL_ATTR_RW(dump_nvme, fid, "0x%08llx\n", "%llx\n",
|
||||
dump_block_nvme->nvme.fid);
|
||||
DEFINE_IPL_ATTR_RW(dump_nvme, nsid, "0x%08llx\n", "%llx\n",
|
||||
dump_block_nvme->nvme.nsid);
|
||||
DEFINE_IPL_ATTR_RW(dump_nvme, bootprog, "%lld\n", "%llx\n",
|
||||
dump_block_nvme->nvme.bootprog);
|
||||
DEFINE_IPL_ATTR_RW(dump_nvme, br_lba, "%lld\n", "%llx\n",
|
||||
dump_block_nvme->nvme.br_lba);
|
||||
DEFINE_IPL_ATTR_BOOTPROG_RW(dump_nvme, bootprog, "%lld\n", "%llx\n",
|
||||
dump_block_nvme->hdr,
|
||||
dump_block_nvme->nvme.bootprog);
|
||||
|
||||
DEFINE_IPL_ATTR_SCP_DATA_RW(dump_nvme, dump_block_nvme->hdr,
|
||||
dump_block_nvme->nvme,
|
||||
@ -1635,8 +1658,9 @@ static const struct attribute_group dump_nvme_attr_group = {
|
||||
|
||||
/* ECKD dump device attributes */
|
||||
DEFINE_IPL_CCW_ATTR_RW(dump_eckd, device, dump_block_eckd->eckd);
|
||||
DEFINE_IPL_ATTR_RW(dump_eckd, bootprog, "%lld\n", "%llx\n",
|
||||
dump_block_eckd->eckd.bootprog);
|
||||
DEFINE_IPL_ATTR_BOOTPROG_RW(dump_eckd, bootprog, "%lld\n", "%llx\n",
|
||||
dump_block_eckd->hdr,
|
||||
dump_block_eckd->eckd.bootprog);
|
||||
|
||||
IPL_ATTR_BR_CHR_SHOW_FN(dump, dump_block_eckd->eckd);
|
||||
IPL_ATTR_BR_CHR_STORE_FN(dump, dump_block_eckd->eckd);
|
||||
|
||||
@ -104,7 +104,6 @@ void arch_stack_walk_user_common(stack_trace_consume_fn consume_entry, void *coo
|
||||
struct stack_frame_vdso_wrapper __user *sf_vdso;
|
||||
struct stack_frame_user __user *sf;
|
||||
unsigned long ip, sp;
|
||||
bool first = true;
|
||||
|
||||
if (!current->mm)
|
||||
return;
|
||||
@ -133,24 +132,11 @@ void arch_stack_walk_user_common(stack_trace_consume_fn consume_entry, void *coo
|
||||
if (__get_user(ip, &sf->gprs[8]))
|
||||
break;
|
||||
}
|
||||
/* Sanity check: ABI requires SP to be 8 byte aligned. */
|
||||
if (sp & 0x7)
|
||||
/* Validate SP and RA (ABI requires SP to be 8 byte aligned). */
|
||||
if (sp & 0x7 || ip_invalid(ip))
|
||||
break;
|
||||
if (ip_invalid(ip)) {
|
||||
/*
|
||||
* If the instruction address is invalid, and this
|
||||
* is the first stack frame, assume r14 has not
|
||||
* been written to the stack yet. Otherwise exit.
|
||||
*/
|
||||
if (!first)
|
||||
break;
|
||||
ip = regs->gprs[14];
|
||||
if (ip_invalid(ip))
|
||||
break;
|
||||
}
|
||||
if (!store_ip(consume_entry, cookie, entry, perf, ip))
|
||||
break;
|
||||
first = false;
|
||||
}
|
||||
pagefault_enable();
|
||||
}
|
||||
|
||||
@ -961,6 +961,7 @@ void zpci_device_reserved(struct zpci_dev *zdev)
|
||||
}
|
||||
|
||||
void zpci_release_device(struct kref *kref)
|
||||
__releases(&zpci_list_lock)
|
||||
{
|
||||
struct zpci_dev *zdev = container_of(kref, struct zpci_dev, kref);
|
||||
|
||||
@ -1148,6 +1149,7 @@ static void zpci_add_devices(struct list_head *scan_list)
|
||||
|
||||
int zpci_scan_devices(void)
|
||||
{
|
||||
struct zpci_bus *zbus;
|
||||
LIST_HEAD(scan_list);
|
||||
int rc;
|
||||
|
||||
@ -1156,7 +1158,10 @@ int zpci_scan_devices(void)
|
||||
return rc;
|
||||
|
||||
zpci_add_devices(&scan_list);
|
||||
zpci_bus_scan_busses();
|
||||
zpci_bus_for_each(zbus) {
|
||||
zpci_bus_scan_bus(zbus);
|
||||
cond_resched();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -153,23 +153,6 @@ int zpci_bus_scan_bus(struct zpci_bus *zbus)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* zpci_bus_scan_busses - Scan all registered busses
|
||||
*
|
||||
* Scan all available zbusses
|
||||
*
|
||||
*/
|
||||
void zpci_bus_scan_busses(void)
|
||||
{
|
||||
struct zpci_bus *zbus = NULL;
|
||||
|
||||
mutex_lock(&zbus_list_lock);
|
||||
list_for_each_entry(zbus, &zbus_list, bus_next) {
|
||||
zpci_bus_scan_bus(zbus);
|
||||
cond_resched();
|
||||
}
|
||||
mutex_unlock(&zbus_list_lock);
|
||||
}
|
||||
|
||||
static bool zpci_bus_is_multifunction_root(struct zpci_dev *zdev)
|
||||
{
|
||||
return !s390_pci_no_rid && zdev->rid_available &&
|
||||
@ -222,10 +205,29 @@ out_free_domain:
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static void zpci_bus_release(struct kref *kref)
|
||||
/**
|
||||
* zpci_bus_release - Un-initialize resources associated with the zbus and
|
||||
* free memory
|
||||
* @kref: refcount * that is part of struct zpci_bus
|
||||
*
|
||||
* MUST be called with `zbus_list_lock` held, but the lock is released during
|
||||
* run of the function.
|
||||
*/
|
||||
static inline void zpci_bus_release(struct kref *kref)
|
||||
__releases(&zbus_list_lock)
|
||||
{
|
||||
struct zpci_bus *zbus = container_of(kref, struct zpci_bus, kref);
|
||||
|
||||
lockdep_assert_held(&zbus_list_lock);
|
||||
|
||||
list_del(&zbus->bus_next);
|
||||
mutex_unlock(&zbus_list_lock);
|
||||
|
||||
/*
|
||||
* At this point no-one should see this object, or be able to get a new
|
||||
* reference to it.
|
||||
*/
|
||||
|
||||
if (zbus->bus) {
|
||||
pci_lock_rescan_remove();
|
||||
pci_stop_root_bus(zbus->bus);
|
||||
@ -237,16 +239,19 @@ static void zpci_bus_release(struct kref *kref)
|
||||
pci_unlock_rescan_remove();
|
||||
}
|
||||
|
||||
mutex_lock(&zbus_list_lock);
|
||||
list_del(&zbus->bus_next);
|
||||
mutex_unlock(&zbus_list_lock);
|
||||
zpci_remove_parent_msi_domain(zbus);
|
||||
kfree(zbus);
|
||||
}
|
||||
|
||||
static void zpci_bus_put(struct zpci_bus *zbus)
|
||||
static inline void __zpci_bus_get(struct zpci_bus *zbus)
|
||||
{
|
||||
kref_put(&zbus->kref, zpci_bus_release);
|
||||
lockdep_assert_held(&zbus_list_lock);
|
||||
kref_get(&zbus->kref);
|
||||
}
|
||||
|
||||
static inline void zpci_bus_put(struct zpci_bus *zbus)
|
||||
{
|
||||
kref_put_mutex(&zbus->kref, zpci_bus_release, &zbus_list_lock);
|
||||
}
|
||||
|
||||
static struct zpci_bus *zpci_bus_get(int topo, bool topo_is_tid)
|
||||
@ -258,7 +263,7 @@ static struct zpci_bus *zpci_bus_get(int topo, bool topo_is_tid)
|
||||
if (!zbus->multifunction)
|
||||
continue;
|
||||
if (topo_is_tid == zbus->topo_is_tid && topo == zbus->topo) {
|
||||
kref_get(&zbus->kref);
|
||||
__zpci_bus_get(zbus);
|
||||
goto out_unlock;
|
||||
}
|
||||
}
|
||||
@ -268,6 +273,44 @@ out_unlock:
|
||||
return zbus;
|
||||
}
|
||||
|
||||
/**
|
||||
* zpci_bus_get_next - get the next zbus object from given position in the list
|
||||
* @pos: current position/cursor in the global zbus list
|
||||
*
|
||||
* Acquires and releases references as the cursor iterates (might also free/
|
||||
* release the cursor). Is tolerant of concurrent operations on the list.
|
||||
*
|
||||
* To begin the iteration, set *@pos to %NULL before calling the function.
|
||||
*
|
||||
* *@pos is set to %NULL in cases where either the list is empty, or *@pos is
|
||||
* the last element in the list.
|
||||
*
|
||||
* Context: Process context. May sleep.
|
||||
*/
|
||||
void zpci_bus_get_next(struct zpci_bus **pos)
|
||||
{
|
||||
struct zpci_bus *curp = *pos, *next = NULL;
|
||||
|
||||
mutex_lock(&zbus_list_lock);
|
||||
if (curp)
|
||||
next = list_next_entry(curp, bus_next);
|
||||
else
|
||||
next = list_first_entry(&zbus_list, typeof(*curp), bus_next);
|
||||
|
||||
if (list_entry_is_head(next, &zbus_list, bus_next))
|
||||
next = NULL;
|
||||
|
||||
if (next)
|
||||
__zpci_bus_get(next);
|
||||
|
||||
*pos = next;
|
||||
mutex_unlock(&zbus_list_lock);
|
||||
|
||||
/* zpci_bus_put() might drop refcount to 0 and locks zbus_list_lock */
|
||||
if (curp)
|
||||
zpci_bus_put(curp);
|
||||
}
|
||||
|
||||
static struct zpci_bus *zpci_bus_alloc(int topo, bool topo_is_tid)
|
||||
{
|
||||
struct zpci_bus *zbus;
|
||||
@ -279,9 +322,6 @@ static struct zpci_bus *zpci_bus_alloc(int topo, bool topo_is_tid)
|
||||
zbus->topo = topo;
|
||||
zbus->topo_is_tid = topo_is_tid;
|
||||
INIT_LIST_HEAD(&zbus->bus_next);
|
||||
mutex_lock(&zbus_list_lock);
|
||||
list_add_tail(&zbus->bus_next, &zbus_list);
|
||||
mutex_unlock(&zbus_list_lock);
|
||||
|
||||
kref_init(&zbus->kref);
|
||||
INIT_LIST_HEAD(&zbus->resources);
|
||||
@ -291,6 +331,10 @@ static struct zpci_bus *zpci_bus_alloc(int topo, bool topo_is_tid)
|
||||
zbus->bus_resource.flags = IORESOURCE_BUS;
|
||||
pci_add_resource(&zbus->resources, &zbus->bus_resource);
|
||||
|
||||
mutex_lock(&zbus_list_lock);
|
||||
list_add_tail(&zbus->bus_next, &zbus_list);
|
||||
mutex_unlock(&zbus_list_lock);
|
||||
|
||||
return zbus;
|
||||
}
|
||||
|
||||
|
||||
@ -15,7 +15,20 @@ int zpci_bus_device_register(struct zpci_dev *zdev, struct pci_ops *ops);
|
||||
void zpci_bus_device_unregister(struct zpci_dev *zdev);
|
||||
|
||||
int zpci_bus_scan_bus(struct zpci_bus *zbus);
|
||||
void zpci_bus_scan_busses(void);
|
||||
void zpci_bus_get_next(struct zpci_bus **pos);
|
||||
|
||||
/**
|
||||
* zpci_bus_for_each - iterate over all the registered zbus objects
|
||||
* @pos: a struct zpci_bus * as cursor
|
||||
*
|
||||
* Acquires and releases references as the cursor iterates over the registered
|
||||
* objects. Is tolerant against concurrent removals of objects.
|
||||
*
|
||||
* Context: Process context. May sleep.
|
||||
*/
|
||||
#define zpci_bus_for_each(pos) \
|
||||
for ((pos) = NULL, zpci_bus_get_next(&(pos)); (pos) != NULL; \
|
||||
zpci_bus_get_next(&(pos)))
|
||||
|
||||
int zpci_bus_scan_device(struct zpci_dev *zdev);
|
||||
void zpci_bus_remove_device(struct zpci_dev *zdev, bool set_error);
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
#include <linux/objtool.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/sort.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <asm/ptrace.h>
|
||||
#include <asm/stacktrace.h>
|
||||
#include <asm/unwind.h>
|
||||
@ -172,6 +173,25 @@ static struct orc_entry *orc_ftrace_find(unsigned long ip)
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Fake frame pointer entry -- used as a fallback for generated code */
|
||||
static struct orc_entry orc_fp_entry = {
|
||||
.type = ORC_TYPE_CALL,
|
||||
.sp_reg = ORC_REG_BP,
|
||||
.sp_offset = 16,
|
||||
.bp_reg = ORC_REG_PREV_SP,
|
||||
.bp_offset = -16,
|
||||
};
|
||||
|
||||
static struct orc_entry *orc_bpf_find(unsigned long ip)
|
||||
{
|
||||
#ifdef CONFIG_BPF_JIT
|
||||
if (bpf_has_frame_pointer(ip))
|
||||
return &orc_fp_entry;
|
||||
#endif
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we crash with IP==0, the last successfully executed instruction
|
||||
* was probably an indirect function call with a NULL function pointer,
|
||||
@ -186,15 +206,6 @@ static struct orc_entry null_orc_entry = {
|
||||
.type = ORC_TYPE_CALL
|
||||
};
|
||||
|
||||
/* Fake frame pointer entry -- used as a fallback for generated code */
|
||||
static struct orc_entry orc_fp_entry = {
|
||||
.type = ORC_TYPE_CALL,
|
||||
.sp_reg = ORC_REG_BP,
|
||||
.sp_offset = 16,
|
||||
.bp_reg = ORC_REG_PREV_SP,
|
||||
.bp_offset = -16,
|
||||
};
|
||||
|
||||
static struct orc_entry *orc_find(unsigned long ip)
|
||||
{
|
||||
static struct orc_entry *orc;
|
||||
@ -238,6 +249,11 @@ static struct orc_entry *orc_find(unsigned long ip)
|
||||
if (orc)
|
||||
return orc;
|
||||
|
||||
/* BPF lookup: */
|
||||
orc = orc_bpf_find(ip);
|
||||
if (orc)
|
||||
return orc;
|
||||
|
||||
return orc_ftrace_find(ip);
|
||||
}
|
||||
|
||||
@ -495,9 +511,8 @@ bool unwind_next_frame(struct unwind_state *state)
|
||||
if (!orc) {
|
||||
/*
|
||||
* As a fallback, try to assume this code uses a frame pointer.
|
||||
* This is useful for generated code, like BPF, which ORC
|
||||
* doesn't know about. This is just a guess, so the rest of
|
||||
* the unwind is no longer considered reliable.
|
||||
* This is just a guess, so the rest of the unwind is no longer
|
||||
* considered reliable.
|
||||
*/
|
||||
orc = &orc_fp_entry;
|
||||
state->error = true;
|
||||
|
||||
@ -1678,6 +1678,9 @@ static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image, u8 *rw_image
|
||||
emit_prologue(&prog, image, stack_depth,
|
||||
bpf_prog_was_classic(bpf_prog), tail_call_reachable,
|
||||
bpf_is_subprog(bpf_prog), bpf_prog->aux->exception_cb);
|
||||
|
||||
bpf_prog->aux->ksym.fp_start = prog - temp;
|
||||
|
||||
/* Exception callback will clobber callee regs for its own use, and
|
||||
* restore the original callee regs from main prog's stack frame.
|
||||
*/
|
||||
@ -2736,6 +2739,8 @@ emit_jmp:
|
||||
pop_r12(&prog);
|
||||
}
|
||||
EMIT1(0xC9); /* leave */
|
||||
bpf_prog->aux->ksym.fp_end = prog - temp;
|
||||
|
||||
emit_return(&prog, image + addrs[i - 1] + (prog - temp));
|
||||
break;
|
||||
|
||||
@ -3325,6 +3330,9 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im
|
||||
}
|
||||
EMIT1(0x55); /* push rbp */
|
||||
EMIT3(0x48, 0x89, 0xE5); /* mov rbp, rsp */
|
||||
if (im)
|
||||
im->ksym.fp_start = prog - (u8 *)rw_image;
|
||||
|
||||
if (!is_imm8(stack_size)) {
|
||||
/* sub rsp, stack_size */
|
||||
EMIT3_off32(0x48, 0x81, 0xEC, stack_size);
|
||||
@ -3462,7 +3470,11 @@ static int __arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *rw_im
|
||||
emit_ldx(&prog, BPF_DW, BPF_REG_0, BPF_REG_FP, -8);
|
||||
|
||||
emit_ldx(&prog, BPF_DW, BPF_REG_6, BPF_REG_FP, -rbx_off);
|
||||
|
||||
EMIT1(0xC9); /* leave */
|
||||
if (im)
|
||||
im->ksym.fp_end = prog - (u8 *)rw_image;
|
||||
|
||||
if (flags & BPF_TRAMP_F_SKIP_FRAME) {
|
||||
/* skip our return address and return to parent */
|
||||
EMIT4(0x48, 0x83, 0xC4, 8); /* add rsp, 8 */
|
||||
|
||||
@ -1283,6 +1283,8 @@ struct bpf_ksym {
|
||||
struct list_head lnode;
|
||||
struct latch_tree_node tnode;
|
||||
bool prog;
|
||||
u32 fp_start;
|
||||
u32 fp_end;
|
||||
};
|
||||
|
||||
enum bpf_tramp_prog_type {
|
||||
@ -1511,6 +1513,7 @@ void bpf_image_ksym_add(struct bpf_ksym *ksym);
|
||||
void bpf_image_ksym_del(struct bpf_ksym *ksym);
|
||||
void bpf_ksym_add(struct bpf_ksym *ksym);
|
||||
void bpf_ksym_del(struct bpf_ksym *ksym);
|
||||
bool bpf_has_frame_pointer(unsigned long ip);
|
||||
int bpf_jit_charge_modmem(u32 size);
|
||||
void bpf_jit_uncharge_modmem(u32 size);
|
||||
bool bpf_prog_has_trampoline(const struct bpf_prog *prog);
|
||||
|
||||
@ -760,6 +760,22 @@ struct bpf_prog *bpf_prog_ksym_find(unsigned long addr)
|
||||
NULL;
|
||||
}
|
||||
|
||||
bool bpf_has_frame_pointer(unsigned long ip)
|
||||
{
|
||||
struct bpf_ksym *ksym;
|
||||
unsigned long offset;
|
||||
|
||||
guard(rcu)();
|
||||
|
||||
ksym = bpf_ksym_find(ip);
|
||||
if (!ksym || !ksym->fp_start || !ksym->fp_end)
|
||||
return false;
|
||||
|
||||
offset = ip - ksym->start;
|
||||
|
||||
return offset >= ksym->fp_start && offset < ksym->fp_end;
|
||||
}
|
||||
|
||||
const struct exception_table_entry *search_bpf_extables(unsigned long addr)
|
||||
{
|
||||
const struct exception_table_entry *e = NULL;
|
||||
|
||||
@ -6,10 +6,33 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/seq_file.h>
|
||||
|
||||
struct dmabuf_iter_priv {
|
||||
/*
|
||||
* If this pointer is non-NULL, the buffer's refcount is elevated to
|
||||
* prevent destruction between stop/start. If reading is not resumed and
|
||||
* start is never called again, then dmabuf_iter_seq_fini drops the
|
||||
* reference when the iterator is released.
|
||||
*/
|
||||
struct dma_buf *dmabuf;
|
||||
};
|
||||
|
||||
static void *dmabuf_iter_seq_start(struct seq_file *seq, loff_t *pos)
|
||||
{
|
||||
if (*pos)
|
||||
return NULL;
|
||||
struct dmabuf_iter_priv *p = seq->private;
|
||||
|
||||
if (*pos) {
|
||||
struct dma_buf *dmabuf = p->dmabuf;
|
||||
|
||||
if (!dmabuf)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* Always resume from where we stopped, regardless of the value
|
||||
* of pos.
|
||||
*/
|
||||
p->dmabuf = NULL;
|
||||
return dmabuf;
|
||||
}
|
||||
|
||||
return dma_buf_iter_begin();
|
||||
}
|
||||
@ -54,8 +77,11 @@ static void dmabuf_iter_seq_stop(struct seq_file *seq, void *v)
|
||||
{
|
||||
struct dma_buf *dmabuf = v;
|
||||
|
||||
if (dmabuf)
|
||||
dma_buf_put(dmabuf);
|
||||
if (dmabuf) {
|
||||
struct dmabuf_iter_priv *p = seq->private;
|
||||
|
||||
p->dmabuf = dmabuf;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct seq_operations dmabuf_iter_seq_ops = {
|
||||
@ -71,11 +97,27 @@ static void bpf_iter_dmabuf_show_fdinfo(const struct bpf_iter_aux_info *aux,
|
||||
seq_puts(seq, "dmabuf iter\n");
|
||||
}
|
||||
|
||||
static int dmabuf_iter_seq_init(void *priv, struct bpf_iter_aux_info *aux)
|
||||
{
|
||||
struct dmabuf_iter_priv *p = (struct dmabuf_iter_priv *)priv;
|
||||
|
||||
p->dmabuf = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dmabuf_iter_seq_fini(void *priv)
|
||||
{
|
||||
struct dmabuf_iter_priv *p = (struct dmabuf_iter_priv *)priv;
|
||||
|
||||
if (p->dmabuf)
|
||||
dma_buf_put(p->dmabuf);
|
||||
}
|
||||
|
||||
static const struct bpf_iter_seq_info dmabuf_iter_seq_info = {
|
||||
.seq_ops = &dmabuf_iter_seq_ops,
|
||||
.init_seq_private = NULL,
|
||||
.fini_seq_private = NULL,
|
||||
.seq_priv_size = 0,
|
||||
.init_seq_private = dmabuf_iter_seq_init,
|
||||
.fini_seq_private = dmabuf_iter_seq_fini,
|
||||
.seq_priv_size = sizeof(struct dmabuf_iter_priv),
|
||||
};
|
||||
|
||||
static struct bpf_iter_reg bpf_dmabuf_reg_info = {
|
||||
|
||||
@ -965,7 +965,7 @@ static const struct bpf_func_proto bpf_d_path_proto = {
|
||||
.ret_type = RET_INTEGER,
|
||||
.arg1_type = ARG_PTR_TO_BTF_ID,
|
||||
.arg1_btf_id = &bpf_d_path_btf_ids[0],
|
||||
.arg2_type = ARG_PTR_TO_MEM,
|
||||
.arg2_type = ARG_PTR_TO_MEM | MEM_WRITE,
|
||||
.arg3_type = ARG_CONST_SIZE_OR_ZERO,
|
||||
.allowed = bpf_d_path_allowed,
|
||||
};
|
||||
|
||||
@ -22,10 +22,10 @@ config SMC_DIAG
|
||||
|
||||
config SMC_HS_CTRL_BPF
|
||||
bool "Generic eBPF hook for SMC handshake flow"
|
||||
depends on SMC && BPF_SYSCALL
|
||||
depends on SMC && BPF_JIT && BPF_SYSCALL
|
||||
default y
|
||||
help
|
||||
SMC_HS_CTRL_BPF enables support to register generic eBPF hook for SMC
|
||||
handshake flow, which offer much greater flexibility in modifying the behavior
|
||||
of the SMC protocol stack compared to a complete kernel-based approach. Select
|
||||
this option if you want filtring the handshake process via eBPF programs.
|
||||
this option if you want filtring the handshake process via eBPF programs.
|
||||
|
||||
@ -224,6 +224,8 @@ endif
|
||||
|
||||
$(OUTPUT)%.bpf.o: skeleton/%.bpf.c $(OUTPUT)vmlinux.h $(LIBBPF_BOOTSTRAP)
|
||||
$(QUIET_CLANG)$(CLANG) \
|
||||
-Wno-microsoft-anon-tag \
|
||||
-fms-extensions \
|
||||
-I$(or $(OUTPUT),.) \
|
||||
-I$(srctree)/tools/include/uapi/ \
|
||||
-I$(LIBBPF_BOOTSTRAP_INCLUDE) \
|
||||
|
||||
@ -8484,7 +8484,7 @@ static int kallsyms_cb(unsigned long long sym_addr, char sym_type,
|
||||
struct bpf_object *obj = ctx;
|
||||
const struct btf_type *t;
|
||||
struct extern_desc *ext;
|
||||
char *res;
|
||||
const char *res;
|
||||
|
||||
res = strstr(sym_name, ".llvm.");
|
||||
if (sym_type == 'd' && res)
|
||||
@ -11818,7 +11818,8 @@ static int avail_kallsyms_cb(unsigned long long sym_addr, char sym_type,
|
||||
*
|
||||
* [0] fb6a421fb615 ("kallsyms: Match symbols exactly with CONFIG_LTO_CLANG")
|
||||
*/
|
||||
char sym_trim[256], *psym_trim = sym_trim, *sym_sfx;
|
||||
char sym_trim[256], *psym_trim = sym_trim;
|
||||
const char *sym_sfx;
|
||||
|
||||
if (!(sym_sfx = strstr(sym_name, ".llvm.")))
|
||||
return 0;
|
||||
@ -12401,7 +12402,7 @@ static int resolve_full_path(const char *file, char *result, size_t result_sz)
|
||||
if (!search_paths[i])
|
||||
continue;
|
||||
for (s = search_paths[i]; s != NULL; s = strchr(s, ':')) {
|
||||
char *next_path;
|
||||
const char *next_path;
|
||||
int seg_len;
|
||||
|
||||
if (s[0] == ':')
|
||||
|
||||
@ -437,6 +437,8 @@ BPF_CFLAGS = -g -Wall -Werror -D__TARGET_ARCH_$(SRCARCH) $(MENDIAN) \
|
||||
-I$(abspath $(OUTPUT)/../usr/include) \
|
||||
-std=gnu11 \
|
||||
-fno-strict-aliasing \
|
||||
-Wno-microsoft-anon-tag \
|
||||
-fms-extensions \
|
||||
-Wno-compare-distinct-pointer-types \
|
||||
-Wno-initializer-overrides \
|
||||
#
|
||||
|
||||
@ -38,6 +38,14 @@ static int set_pathname(int fd, pid_t pid)
|
||||
return readlink(buf, src.paths[src.cnt++], MAX_PATH_LEN);
|
||||
}
|
||||
|
||||
static inline long syscall_close(int fd)
|
||||
{
|
||||
return syscall(__NR_close_range,
|
||||
(unsigned int)fd,
|
||||
(unsigned int)fd,
|
||||
0u);
|
||||
}
|
||||
|
||||
static int trigger_fstat_events(pid_t pid)
|
||||
{
|
||||
int sockfd = -1, procfd = -1, devfd = -1;
|
||||
@ -104,36 +112,47 @@ out_close:
|
||||
/* sys_close no longer triggers filp_close, but we can
|
||||
* call sys_close_range instead which still does
|
||||
*/
|
||||
#define close(fd) syscall(__NR_close_range, fd, fd, 0)
|
||||
|
||||
close(pipefd[0]);
|
||||
close(pipefd[1]);
|
||||
close(sockfd);
|
||||
close(procfd);
|
||||
close(devfd);
|
||||
close(localfd);
|
||||
close(indicatorfd);
|
||||
|
||||
#undef close
|
||||
syscall_close(pipefd[0]);
|
||||
syscall_close(pipefd[1]);
|
||||
syscall_close(sockfd);
|
||||
syscall_close(procfd);
|
||||
syscall_close(devfd);
|
||||
syscall_close(localfd);
|
||||
syscall_close(indicatorfd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void attach_and_load(struct test_d_path **skel)
|
||||
{
|
||||
int err;
|
||||
|
||||
*skel = test_d_path__open_and_load();
|
||||
if (CHECK(!*skel, "setup", "d_path skeleton failed\n"))
|
||||
goto cleanup;
|
||||
|
||||
err = test_d_path__attach(*skel);
|
||||
if (CHECK(err, "setup", "attach failed: %d\n", err))
|
||||
goto cleanup;
|
||||
|
||||
(*skel)->bss->my_pid = getpid();
|
||||
return;
|
||||
|
||||
cleanup:
|
||||
test_d_path__destroy(*skel);
|
||||
*skel = NULL;
|
||||
}
|
||||
|
||||
static void test_d_path_basic(void)
|
||||
{
|
||||
struct test_d_path__bss *bss;
|
||||
struct test_d_path *skel;
|
||||
int err;
|
||||
|
||||
skel = test_d_path__open_and_load();
|
||||
if (CHECK(!skel, "setup", "d_path skeleton failed\n"))
|
||||
goto cleanup;
|
||||
|
||||
err = test_d_path__attach(skel);
|
||||
if (CHECK(err, "setup", "attach failed: %d\n", err))
|
||||
attach_and_load(&skel);
|
||||
if (!skel)
|
||||
goto cleanup;
|
||||
|
||||
bss = skel->bss;
|
||||
bss->my_pid = getpid();
|
||||
|
||||
err = trigger_fstat_events(bss->my_pid);
|
||||
if (err < 0)
|
||||
@ -195,6 +214,39 @@ static void test_d_path_check_types(void)
|
||||
test_d_path_check_types__destroy(skel);
|
||||
}
|
||||
|
||||
/* Check if the verifier correctly generates code for
|
||||
* accessing the memory modified by d_path helper.
|
||||
*/
|
||||
static void test_d_path_mem_access(void)
|
||||
{
|
||||
int localfd = -1;
|
||||
char path_template[] = "/dev/shm/d_path_loadgen.XXXXXX";
|
||||
struct test_d_path__bss *bss;
|
||||
struct test_d_path *skel;
|
||||
|
||||
attach_and_load(&skel);
|
||||
if (!skel)
|
||||
goto cleanup;
|
||||
|
||||
bss = skel->bss;
|
||||
|
||||
localfd = mkstemp(path_template);
|
||||
if (CHECK(localfd < 0, "trigger", "mkstemp failed\n"))
|
||||
goto cleanup;
|
||||
|
||||
if (CHECK(fallocate(localfd, 0, 0, 1024) < 0, "trigger", "fallocate failed\n"))
|
||||
goto cleanup;
|
||||
remove(path_template);
|
||||
|
||||
if (CHECK(!bss->path_match_fallocate, "check",
|
||||
"failed to read fallocate path"))
|
||||
goto cleanup;
|
||||
|
||||
cleanup:
|
||||
syscall_close(localfd);
|
||||
test_d_path__destroy(skel);
|
||||
}
|
||||
|
||||
void test_d_path(void)
|
||||
{
|
||||
if (test__start_subtest("basic"))
|
||||
@ -205,4 +257,7 @@ void test_d_path(void)
|
||||
|
||||
if (test__start_subtest("check_alloc_mem"))
|
||||
test_d_path_check_types();
|
||||
|
||||
if (test__start_subtest("check_mem_access"))
|
||||
test_d_path_mem_access();
|
||||
}
|
||||
|
||||
@ -73,12 +73,10 @@ close_memfd:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int create_sys_heap_dmabuf(void)
|
||||
static int create_sys_heap_dmabuf(size_t bytes)
|
||||
{
|
||||
sysheap_test_buffer_size = 20 * getpagesize();
|
||||
|
||||
struct dma_heap_allocation_data data = {
|
||||
.len = sysheap_test_buffer_size,
|
||||
.len = bytes,
|
||||
.fd = 0,
|
||||
.fd_flags = O_RDWR | O_CLOEXEC,
|
||||
.heap_flags = 0,
|
||||
@ -110,7 +108,9 @@ close_sysheap_dmabuf:
|
||||
static int create_test_buffers(void)
|
||||
{
|
||||
udmabuf = create_udmabuf();
|
||||
sysheap_dmabuf = create_sys_heap_dmabuf();
|
||||
|
||||
sysheap_test_buffer_size = 20 * getpagesize();
|
||||
sysheap_dmabuf = create_sys_heap_dmabuf(sysheap_test_buffer_size);
|
||||
|
||||
if (udmabuf < 0 || sysheap_dmabuf < 0)
|
||||
return -1;
|
||||
@ -219,6 +219,26 @@ close_iter_fd:
|
||||
close(iter_fd);
|
||||
}
|
||||
|
||||
static void subtest_dmabuf_iter_check_lots_of_buffers(struct dmabuf_iter *skel)
|
||||
{
|
||||
int iter_fd;
|
||||
char buf[1024];
|
||||
size_t total_bytes_read = 0;
|
||||
ssize_t bytes_read;
|
||||
|
||||
iter_fd = bpf_iter_create(bpf_link__fd(skel->links.dmabuf_collector));
|
||||
if (!ASSERT_OK_FD(iter_fd, "iter_create"))
|
||||
return;
|
||||
|
||||
while ((bytes_read = read(iter_fd, buf, sizeof(buf))) > 0)
|
||||
total_bytes_read += bytes_read;
|
||||
|
||||
ASSERT_GT(total_bytes_read, getpagesize(), "total_bytes_read");
|
||||
|
||||
close(iter_fd);
|
||||
}
|
||||
|
||||
|
||||
static void subtest_dmabuf_iter_check_open_coded(struct dmabuf_iter *skel, int map_fd)
|
||||
{
|
||||
LIBBPF_OPTS(bpf_test_run_opts, topts);
|
||||
@ -275,6 +295,23 @@ void test_dmabuf_iter(void)
|
||||
subtest_dmabuf_iter_check_no_infinite_reads(skel);
|
||||
if (test__start_subtest("default_iter"))
|
||||
subtest_dmabuf_iter_check_default_iter(skel);
|
||||
if (test__start_subtest("lots_of_buffers")) {
|
||||
size_t NUM_BUFS = 100;
|
||||
int buffers[NUM_BUFS];
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NUM_BUFS; ++i) {
|
||||
buffers[i] = create_sys_heap_dmabuf(getpagesize());
|
||||
if (!ASSERT_OK_FD(buffers[i], "dmabuf_fd"))
|
||||
goto cleanup_bufs;
|
||||
}
|
||||
|
||||
subtest_dmabuf_iter_check_lots_of_buffers(skel);
|
||||
|
||||
cleanup_bufs:
|
||||
for (--i; i >= 0; --i)
|
||||
close(buffers[i]);
|
||||
}
|
||||
if (test__start_subtest("open_coded"))
|
||||
subtest_dmabuf_iter_check_open_coded(skel, map_fd);
|
||||
|
||||
|
||||
@ -17,6 +17,7 @@ int rets_close[MAX_FILES] = {};
|
||||
|
||||
int called_stat = 0;
|
||||
int called_close = 0;
|
||||
int path_match_fallocate = 0;
|
||||
|
||||
SEC("fentry/security_inode_getattr")
|
||||
int BPF_PROG(prog_stat, struct path *path, struct kstat *stat,
|
||||
@ -62,4 +63,26 @@ int BPF_PROG(prog_close, struct file *file, void *id)
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("fentry/vfs_fallocate")
|
||||
int BPF_PROG(prog_fallocate, struct file *file, int mode, loff_t offset, loff_t len)
|
||||
{
|
||||
pid_t pid = bpf_get_current_pid_tgid() >> 32;
|
||||
int ret = 0;
|
||||
char path_fallocate[MAX_PATH_LEN] = {};
|
||||
|
||||
if (pid != my_pid)
|
||||
return 0;
|
||||
|
||||
ret = bpf_d_path(&file->f_path,
|
||||
path_fallocate, MAX_PATH_LEN);
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
|
||||
if (!path_fallocate[0])
|
||||
return 0;
|
||||
|
||||
path_match_fallocate = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user