* refs/heads/tmp-6f0b5b5:
Linux 4.9.207
net: stmmac: don't stop NAPI processing when dropping a packet
net: stmmac: use correct DMA buffer size in the RX descriptor
xhci: fix USB3 device initiated resume race with roothub autosuspend
drm/radeon: fix r1xx/r2xx register checker for POT textures
scsi: iscsi: Fix a potential deadlock in the timeout handler
dm btree: increase rebalance threshold in __rebalance2()
dma-buf: Fix memory leak in sync_file_merge()
vfio/pci: call irq_bypass_unregister_producer() before freeing irq
ARM: tegra: Fix FLOW_CTLR_HALT register clobbering by tegra_resume()
ARM: dts: s3c64xx: Fix init order of clock providers
CIFS: Respect O_SYNC and O_DIRECT flags during reconnect
xtensa: fix TLB sanity checker
PCI/MSI: Fix incorrect MSI-X masking on resume
PCI: Fix Intel ACS quirk UPDCR register address
Revert "regulator: Defer init completion for a while after late_initcall"
tcp: Protect accesses to .ts_recent_stamp with {READ,WRITE}_ONCE()
tcp: tighten acceptance of ACKs not matching a child socket
tcp: fix rejected syncookies due to stale timestamps
inet: protect against too small mtu values.
tipc: fix ordering of tipc module init and exit routine
tcp: md5: fix potential overestimation of TCP option space
openvswitch: support asymmetric conntrack
net: ethernet: ti: cpsw: fix extra rx interrupt
net: bridge: deny dev_set_mac_address() when unregistering
nvme: host: core: fix precedence of ternary operator
kernel/module.c: wakeup processes in module_wq on module unload
net/mlx5e: Fix SFF 8472 eeprom length
sunrpc: fix crash when cache_head become valid before update
workqueue: Fix missing kfree(rescuer) in destroy_workqueue()
blk-mq: make sure that line break can be printed
ext4: fix a bug in ext4_wait_for_tail_page_commit
mm/shmem.c: cast the type of unmap_start to u64
firmware: qcom: scm: Ensure 'a0' status code is treated as signed
reiserfs: fix extended attributes on the root directory
powerpc: Fix vDSO clock_getres()
scsi: qla2xxx: Always check the qla2x00_wait_for_hba_online() return value
scsi: qla2xxx: Fix qla24xx_process_bidir_cmd()
scsi: qla2xxx: Fix session lookup in qlt_abort_work()
scsi: qla2xxx: Fix DMA unmap leak
pinctrl: samsung: Fix device node refcount leaks in S3C64xx wakeup controller init
ARM: dts: omap3-tao3530: Fix incorrect MMC card detection GPIO polarity
ath10k: fix fw crash by moving chip reset after napi disabled
x86/MCE/AMD: Carve out the MC4_MISC thresholding quirk
x86/MCE/AMD: Turn off MC4_MISC thresholding on all family 0x15 models
e100: Fix passing zero to 'PTR_ERR' warning in e100_load_ucode_wait
scsi: lpfc: Cap NPIV vports to 256
omap: pdata-quirks: remove openpandora quirks for mmc3 and wl1251
Btrfs: fix negative subv_writers counter and data space leak after buffered write
iio: adis16480: Add debugfs_reg_access entry
xhci: make sure interrupts are restored to correct state
xhci: Fix memory leak in xhci_add_in_port()
usb: xhci: only set D3hot for pci device
scsi: zfcp: trace channel log even for FCP command responses
quota: fix livelock in dquot_writeback_dquots
ext2: check err when partial != NULL
quota: Check that quota is not dirty before release
video/hdmi: Fix AVI bar unpack
powerpc: Allow 64bit VDSO __kernel_sync_dicache to work across ranges >4GB
ppdev: fix PPGETTIME/PPSETTIME ioctls
mmc: host: omap_hsmmc: add code for special init of wl1251 to get rid of pandora_wl1251_init_card
pinctrl: samsung: Fix device node refcount leaks in init code
pinctrl: samsung: Fix device node refcount leaks in S3C24xx wakeup controller init
ACPI: PM: Avoid attaching ACPI PM domain to certain devices
ACPI: bus: Fix NULL pointer check in acpi_bus_get_private_data()
ACPI: OSL: only free map once in osl.c
PM / devfreq: Lock devfreq in trans_stat_show
cpuidle: Do not unset the driver if it is there already
media: radio: wl1273: fix interrupt masking on release
media: bdisp: fix memleak on release
ar5523: check NULL before memcpy() in ar5523_cmd()
cgroup: pids: use atomic64_t for pids->limit
blk-mq: avoid sysfs buffer overflow with too many CPU cores
ASoC: Jack: Fix NULL pointer dereference in snd_soc_jack_report
workqueue: Fix pwq ref leak in rescuer_thread()
workqueue: Fix spurious sanity check failures in destroy_workqueue()
ALSA: hda - Fix pending unsol events at shutdown
lib: raid6: fix awk build warnings
rtlwifi: rtl8192de: Fix missing enable interrupt flag
rtlwifi: rtl8192de: Fix missing callback that tests for hw release of buffer
rtlwifi: rtl8192de: Fix missing code to retrieve RX buffer address
btrfs: record all roots for rename exchange on a subvol
Btrfs: send, skip backreference walking for extents with many references
btrfs: Remove btrfs_bio::flags member
btrfs: check page->mapping when loading free space cache
virtio-balloon: fix managed page counts when migrating pages between zones
mtd: spear_smi: Fix Write Burst mode
usb: mon: Fix a deadlock in usbmon between mmap and read
usb: core: urb: fix URB structure initialization function
USB: adutux: fix interface sanity check
USB: serial: io_edgeport: fix epic endpoint lookup
USB: idmouse: fix interface sanity checks
USB: atm: ueagle-atm: add missing endpoint check
iio: humidity: hdc100x: fix IIO_HUMIDITYRELATIVE channel reporting
ARM: dts: pandora-common: define wl1251 as child node of mmc3
xhci: Increase STS_HALT timeout in xhci_suspend()
staging: gigaset: add endpoint-type sanity check
staging: gigaset: fix illegal free on probe errors
staging: gigaset: fix general protection fault on probe
staging: rtl8712: fix interface sanity check
staging: rtl8188eu: fix interface sanity check
usb: Allow USB device to be warm reset in suspended state
USB: uas: heed CAPACITY_HEURISTICS
USB: uas: honor flag to avoid CAPACITY16
usb: gadget: configfs: Fix missing spin_lock_init()
appletalk: Set error code if register_snap_client failed
appletalk: Fix potential NULL pointer dereference in unregister_snap_client
KVM: x86: fix out-of-bounds write in KVM_GET_EMULATED_CPUID (CVE-2019-19332)
thermal: Fix deadlock in thermal thermal_zone_device_check
RDMA/qib: Validate ->show()/store() callbacks before calling them
spi: atmel: Fix CS high support
crypto: user - fix memory leak in crypto_report
crypto: ecdh - fix big endian bug in ECC library
crypto: ccp - fix uninitialized list head
crypto: crypto4xx - fix double-free in crypto4xx_destroy_sdr
KVM: x86: fix presentation of TSX feature in ARCH_CAPABILITIES
KVM: x86: do not modify masked bits of shared MSRs
drm/i810: Prevent underflow in ioctl
jbd2: Fix possible overflow in jbd2_log_space_left()
can: slcan: Fix use-after-free Read in slcan_open
tty: vt: keyboard: reject invalid keycodes
CIFS: Fix SMB2 oplock break processing
CIFS: Fix NULL-pointer dereference in smb2_push_mandatory_locks
x86/PCI: Avoid AMD FCH XHCI USB PME# from D0 defect
coresight: etm4x: Fix input validation for sysfs.
Input: goodix - add upside-down quirk for Teclast X89 tablet
ALSA: pcm: oss: Avoid potential buffer overflows
fuse: verify attributes
fuse: verify nlink
sched/fair: Scale bandwidth quota and period without losing quota/period ratio precision
ARM: dts: sunxi: Fix PMU compatible strings
mlx4: Use snprintf instead of complicated strcpy
media: stkwebcam: Bugfix for wrong return values
tty: Don't block on IO when ldisc change is pending
nfsd: Return EPERM, not EACCES, in some SETATTR cases
MIPS: OCTEON: cvmx_pko_mem_debug8: use oldest forward compatible definition
powerpc/math-emu: Update macros from GCC
net/mlx4_core: Fix return codes of unsupported operations
dlm: fix invalid cluster name warning
ARM: dts: realview: Fix some more duplicate regulator nodes
clk: sunxi-ng: h3/h5: Fix CSI_MCLK parent
ARM: dts: pxa: clean up USB controller nodes
mtd: fix mtd_oobavail() incoherent returned value
kbuild: fix single target build for external module
modpost: skip ELF local symbols during section mismatch check
tcp: fix SNMP TCP timeout under-estimation
tcp: fix off-by-one bug on aborting window-probing socket
ARM: dts: realview-pbx: Fix duplicate regulator nodes
ARM: dts: mmp2: fix the gpio interrupt cell number
net/x25: fix null_x25_address handling
net/x25: fix called/calling length calculation in x25_parse_address_block
ARM: OMAP1/2: fix SoC name printing
nfsd: fix a warning in __cld_pipe_upcall()
ARM: debug: enable UART1 for socfpga Cyclone5
dlm: NULL check before kmem_cache_destroy is not needed
i2c: imx: don't print error message on probe defer
serial: imx: fix error handling in console_setup
altera-stapl: check for a null key before strcasecmp'ing it
dma-mapping: fix return type of dma_set_max_seg_size()
ACPI: fix acpi_find_child_device() invocation in acpi_preset_companion()
usb: dwc3: don't log probe deferrals; but do log other error codes
dmaengine: coh901318: Remove unused variable
dmaengine: coh901318: Fix a double-lock bug
media: pulse8-cec: return 0 when invalidating the logical address
ARM: dts: exynos: Use Samsung SoC specific compatible for DWC2 module
rtc: dt-binding: abx80x: fix resistance scale
rtc: max8997: Fix the returned value in case of error in 'max8997_rtc_read_alarm()'
math-emu/soft-fp.h: (_FP_ROUND_ZERO) cast 0 to void to fix warning
MIPS: OCTEON: octeon-platform: fix typing
regulator: Fix return value of _set_load() stub
Staging: iio: adt7316: Fix i2c data reading, set the data field
pinctrl: qcom: ssbi-gpio: fix gpio-hog related boot issues
scsi: zfcp: drop default switch case which might paper over missing case
MIPS: SiByte: Enable ZONE_DMA32 for LittleSur
dlm: fix missing idr_destroy for recover_idr
ARM: dts: rockchip: Fix rk3288-rock2 vcc_flash name
clk: rockchip: fix rk3188 sclk_mac_lbtest parameter ordering
clk: rockchip: fix rk3188 sclk_smc gate data
extcon: max8997: Fix lack of path setting in USB device mode
net/mlx5: Release resource on error flow
ARM: 8813/1: Make aligned 2-byte getuser()/putuser() atomic on ARMv6+
iwlwifi: mvm: Send non offchannel traffic via AP sta
cxgb4vf: fix memleak in mac_hlist initialization
serial: core: Allow processing sysrq at port unlock time
net: ep93xx_eth: fix mismatch of request_mem_region in remove
rsxx: add missed destroy_workqueue calls in remove
ALSA: pcm: Fix stream lock usage in snd_pcm_period_elapsed()
Input: cyttsp4_core - fix use after free bug
NFC: nxp-nci: Fix NULL pointer dereference after I2C communication error
audit_get_nd(): don't unlock parent too early
exportfs_decode_fh(): negative pinned may become positive without the parent locked
RDMA/hns: Correct the value of HNS_ROCE_HEM_CHUNK_LEN
autofs: fix a leak in autofs_expire_indirect()
serial: ifx6x60: add missed pm_runtime_disable
serial: serial_core: Perform NULL checks for break_ctl ops
serial: pl011: Fix DMA ->flush_buffer()
tty: serial: msm_serial: Fix flow control
tty: serial: fsl_lpuart: use the sg count from dma_map_sg
usb: gadget: u_serial: add missing port entry locking
arm64: tegra: Fix 'active-low' warning for Jetson TX1 regulator
UPSTREAM: binder: fix incorrect calculation for num_valid
ANDROID: sched/core: Fix arm32 allmodconfig build-break
BACKPORT: bpf: permit multiple bpf attachments for a single perf event
UPSTREAM: bpf: use the same condition in perf event set/free bpf handler
BACKPORT: bpf: multi program support for cgroup+bpf
Conflicts:
drivers/usb/dwc3/core.c
drivers/usb/host/xhci.c
include/trace/events/sched.h
Change-Id: Ida9ef3977cd175731c312341385819f5812f707c
Signed-off-by: jianzhou <jianzhou@codeaurora.org>
768 lines
19 KiB
C
768 lines
19 KiB
C
#include <trace/syscall.h>
|
|
#include <trace/events/syscalls.h>
|
|
#include <linux/syscalls.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h> /* for MODULE_NAME_LEN via KSYM_SYMBOL_LEN */
|
|
#include <linux/ftrace.h>
|
|
#include <linux/perf_event.h>
|
|
#include <asm/syscall.h>
|
|
|
|
#include "trace_output.h"
|
|
#include "trace.h"
|
|
|
|
static DEFINE_MUTEX(syscall_trace_lock);
|
|
|
|
static int syscall_enter_register(struct trace_event_call *event,
|
|
enum trace_reg type, void *data);
|
|
static int syscall_exit_register(struct trace_event_call *event,
|
|
enum trace_reg type, void *data);
|
|
|
|
static struct list_head *
|
|
syscall_get_enter_fields(struct trace_event_call *call)
|
|
{
|
|
struct syscall_metadata *entry = call->data;
|
|
|
|
return &entry->enter_fields;
|
|
}
|
|
|
|
extern struct syscall_metadata *__start_syscalls_metadata[];
|
|
extern struct syscall_metadata *__stop_syscalls_metadata[];
|
|
|
|
static struct syscall_metadata **syscalls_metadata;
|
|
|
|
#ifndef ARCH_HAS_SYSCALL_MATCH_SYM_NAME
|
|
static inline bool arch_syscall_match_sym_name(const char *sym, const char *name)
|
|
{
|
|
/*
|
|
* Only compare after the "sys" prefix. Archs that use
|
|
* syscall wrappers may have syscalls symbols aliases prefixed
|
|
* with ".SyS" or ".sys" instead of "sys", leading to an unwanted
|
|
* mismatch.
|
|
*/
|
|
return !strcmp(sym + 3, name + 3);
|
|
}
|
|
#endif
|
|
|
|
#ifdef ARCH_TRACE_IGNORE_COMPAT_SYSCALLS
|
|
/*
|
|
* Some architectures that allow for 32bit applications
|
|
* to run on a 64bit kernel, do not map the syscalls for
|
|
* the 32bit tasks the same as they do for 64bit tasks.
|
|
*
|
|
* *cough*x86*cough*
|
|
*
|
|
* In such a case, instead of reporting the wrong syscalls,
|
|
* simply ignore them.
|
|
*
|
|
* For an arch to ignore the compat syscalls it needs to
|
|
* define ARCH_TRACE_IGNORE_COMPAT_SYSCALLS as well as
|
|
* define the function arch_trace_is_compat_syscall() to let
|
|
* the tracing system know that it should ignore it.
|
|
*/
|
|
static int
|
|
trace_get_syscall_nr(struct task_struct *task, struct pt_regs *regs)
|
|
{
|
|
if (unlikely(arch_trace_is_compat_syscall(regs)))
|
|
return -1;
|
|
|
|
return syscall_get_nr(task, regs);
|
|
}
|
|
#else
|
|
static inline int
|
|
trace_get_syscall_nr(struct task_struct *task, struct pt_regs *regs)
|
|
{
|
|
return syscall_get_nr(task, regs);
|
|
}
|
|
#endif /* ARCH_TRACE_IGNORE_COMPAT_SYSCALLS */
|
|
|
|
static __init struct syscall_metadata *
|
|
find_syscall_meta(unsigned long syscall)
|
|
{
|
|
struct syscall_metadata **start;
|
|
struct syscall_metadata **stop;
|
|
char str[KSYM_SYMBOL_LEN];
|
|
|
|
|
|
start = __start_syscalls_metadata;
|
|
stop = __stop_syscalls_metadata;
|
|
kallsyms_lookup(syscall, NULL, NULL, NULL, str);
|
|
|
|
if (arch_syscall_match_sym_name(str, "sys_ni_syscall"))
|
|
return NULL;
|
|
|
|
for ( ; start < stop; start++) {
|
|
if ((*start)->name && arch_syscall_match_sym_name(str, (*start)->name))
|
|
return *start;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static struct syscall_metadata *syscall_nr_to_meta(int nr)
|
|
{
|
|
if (!syscalls_metadata || nr >= NR_syscalls || nr < 0)
|
|
return NULL;
|
|
|
|
return syscalls_metadata[nr];
|
|
}
|
|
|
|
const char *get_syscall_name(int syscall)
|
|
{
|
|
struct syscall_metadata *entry;
|
|
|
|
entry = syscall_nr_to_meta(syscall);
|
|
if (!entry)
|
|
return NULL;
|
|
|
|
return entry->name;
|
|
}
|
|
|
|
static enum print_line_t
|
|
print_syscall_enter(struct trace_iterator *iter, int flags,
|
|
struct trace_event *event)
|
|
{
|
|
struct trace_array *tr = iter->tr;
|
|
struct trace_seq *s = &iter->seq;
|
|
struct trace_entry *ent = iter->ent;
|
|
struct syscall_trace_enter *trace;
|
|
struct syscall_metadata *entry;
|
|
int i, syscall;
|
|
|
|
trace = (typeof(trace))ent;
|
|
syscall = trace->nr;
|
|
entry = syscall_nr_to_meta(syscall);
|
|
|
|
if (!entry)
|
|
goto end;
|
|
|
|
if (entry->enter_event->event.type != ent->type) {
|
|
WARN_ON_ONCE(1);
|
|
goto end;
|
|
}
|
|
|
|
trace_seq_printf(s, "%s(", entry->name);
|
|
|
|
for (i = 0; i < entry->nb_args; i++) {
|
|
|
|
if (trace_seq_has_overflowed(s))
|
|
goto end;
|
|
|
|
/* parameter types */
|
|
if (tr->trace_flags & TRACE_ITER_VERBOSE)
|
|
trace_seq_printf(s, "%s ", entry->types[i]);
|
|
|
|
/* parameter values */
|
|
trace_seq_printf(s, "%s: %lx%s", entry->args[i],
|
|
trace->args[i],
|
|
i == entry->nb_args - 1 ? "" : ", ");
|
|
}
|
|
|
|
trace_seq_putc(s, ')');
|
|
end:
|
|
trace_seq_putc(s, '\n');
|
|
|
|
return trace_handle_return(s);
|
|
}
|
|
|
|
static enum print_line_t
|
|
print_syscall_exit(struct trace_iterator *iter, int flags,
|
|
struct trace_event *event)
|
|
{
|
|
struct trace_seq *s = &iter->seq;
|
|
struct trace_entry *ent = iter->ent;
|
|
struct syscall_trace_exit *trace;
|
|
int syscall;
|
|
struct syscall_metadata *entry;
|
|
|
|
trace = (typeof(trace))ent;
|
|
syscall = trace->nr;
|
|
entry = syscall_nr_to_meta(syscall);
|
|
|
|
if (!entry) {
|
|
trace_seq_putc(s, '\n');
|
|
goto out;
|
|
}
|
|
|
|
if (entry->exit_event->event.type != ent->type) {
|
|
WARN_ON_ONCE(1);
|
|
return TRACE_TYPE_UNHANDLED;
|
|
}
|
|
|
|
trace_seq_printf(s, "%s -> 0x%lx\n", entry->name,
|
|
trace->ret);
|
|
|
|
out:
|
|
return trace_handle_return(s);
|
|
}
|
|
|
|
extern char *__bad_type_size(void);
|
|
|
|
#define SYSCALL_FIELD(type, field, name) \
|
|
sizeof(type) != sizeof(trace.field) ? \
|
|
__bad_type_size() : \
|
|
#type, #name, offsetof(typeof(trace), field), \
|
|
sizeof(trace.field), is_signed_type(type)
|
|
|
|
static int __init
|
|
__set_enter_print_fmt(struct syscall_metadata *entry, char *buf, int len)
|
|
{
|
|
int i;
|
|
int pos = 0;
|
|
|
|
/* When len=0, we just calculate the needed length */
|
|
#define LEN_OR_ZERO (len ? len - pos : 0)
|
|
|
|
pos += snprintf(buf + pos, LEN_OR_ZERO, "\"");
|
|
for (i = 0; i < entry->nb_args; i++) {
|
|
pos += snprintf(buf + pos, LEN_OR_ZERO, "%s: 0x%%0%zulx%s",
|
|
entry->args[i], sizeof(unsigned long),
|
|
i == entry->nb_args - 1 ? "" : ", ");
|
|
}
|
|
pos += snprintf(buf + pos, LEN_OR_ZERO, "\"");
|
|
|
|
for (i = 0; i < entry->nb_args; i++) {
|
|
pos += snprintf(buf + pos, LEN_OR_ZERO,
|
|
", ((unsigned long)(REC->%s))", entry->args[i]);
|
|
}
|
|
|
|
#undef LEN_OR_ZERO
|
|
|
|
/* return the length of print_fmt */
|
|
return pos;
|
|
}
|
|
|
|
static int __init set_syscall_print_fmt(struct trace_event_call *call)
|
|
{
|
|
char *print_fmt;
|
|
int len;
|
|
struct syscall_metadata *entry = call->data;
|
|
|
|
if (entry->enter_event != call) {
|
|
call->print_fmt = "\"0x%lx\", REC->ret";
|
|
return 0;
|
|
}
|
|
|
|
/* First: called with 0 length to calculate the needed length */
|
|
len = __set_enter_print_fmt(entry, NULL, 0);
|
|
|
|
print_fmt = kmalloc(len + 1, GFP_KERNEL);
|
|
if (!print_fmt)
|
|
return -ENOMEM;
|
|
|
|
/* Second: actually write the @print_fmt */
|
|
__set_enter_print_fmt(entry, print_fmt, len + 1);
|
|
call->print_fmt = print_fmt;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void __init free_syscall_print_fmt(struct trace_event_call *call)
|
|
{
|
|
struct syscall_metadata *entry = call->data;
|
|
|
|
if (entry->enter_event == call)
|
|
kfree(call->print_fmt);
|
|
}
|
|
|
|
static int __init syscall_enter_define_fields(struct trace_event_call *call)
|
|
{
|
|
struct syscall_trace_enter trace;
|
|
struct syscall_metadata *meta = call->data;
|
|
int ret;
|
|
int i;
|
|
int offset = offsetof(typeof(trace), args);
|
|
|
|
ret = trace_define_field(call, SYSCALL_FIELD(int, nr, __syscall_nr),
|
|
FILTER_OTHER);
|
|
if (ret)
|
|
return ret;
|
|
|
|
for (i = 0; i < meta->nb_args; i++) {
|
|
ret = trace_define_field(call, meta->types[i],
|
|
meta->args[i], offset,
|
|
sizeof(unsigned long), 0,
|
|
FILTER_OTHER);
|
|
offset += sizeof(unsigned long);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int __init syscall_exit_define_fields(struct trace_event_call *call)
|
|
{
|
|
struct syscall_trace_exit trace;
|
|
int ret;
|
|
|
|
ret = trace_define_field(call, SYSCALL_FIELD(int, nr, __syscall_nr),
|
|
FILTER_OTHER);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = trace_define_field(call, SYSCALL_FIELD(long, ret, ret),
|
|
FILTER_OTHER);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void ftrace_syscall_enter(void *data, struct pt_regs *regs, long id)
|
|
{
|
|
struct trace_array *tr = data;
|
|
struct trace_event_file *trace_file;
|
|
struct syscall_trace_enter *entry;
|
|
struct syscall_metadata *sys_data;
|
|
struct ring_buffer_event *event;
|
|
struct ring_buffer *buffer;
|
|
unsigned long irq_flags;
|
|
int pc;
|
|
int syscall_nr;
|
|
int size;
|
|
|
|
syscall_nr = trace_get_syscall_nr(current, regs);
|
|
if (syscall_nr < 0 || syscall_nr >= NR_syscalls)
|
|
return;
|
|
|
|
/* Here we're inside tp handler's rcu_read_lock_sched (__DO_TRACE) */
|
|
trace_file = rcu_dereference_sched(tr->enter_syscall_files[syscall_nr]);
|
|
if (!trace_file)
|
|
return;
|
|
|
|
if (trace_trigger_soft_disabled(trace_file))
|
|
return;
|
|
|
|
sys_data = syscall_nr_to_meta(syscall_nr);
|
|
if (!sys_data)
|
|
return;
|
|
|
|
size = sizeof(*entry) + sizeof(unsigned long) * sys_data->nb_args;
|
|
|
|
local_save_flags(irq_flags);
|
|
pc = preempt_count();
|
|
|
|
buffer = tr->trace_buffer.buffer;
|
|
event = trace_buffer_lock_reserve(buffer,
|
|
sys_data->enter_event->event.type, size, irq_flags, pc);
|
|
if (!event)
|
|
return;
|
|
|
|
entry = ring_buffer_event_data(event);
|
|
entry->nr = syscall_nr;
|
|
syscall_get_arguments(current, regs, 0, sys_data->nb_args, entry->args);
|
|
|
|
event_trigger_unlock_commit(trace_file, buffer, event, entry,
|
|
irq_flags, pc, 0);
|
|
}
|
|
|
|
static void ftrace_syscall_exit(void *data, struct pt_regs *regs, long ret)
|
|
{
|
|
struct trace_array *tr = data;
|
|
struct trace_event_file *trace_file;
|
|
struct syscall_trace_exit *entry;
|
|
struct syscall_metadata *sys_data;
|
|
struct ring_buffer_event *event;
|
|
struct ring_buffer *buffer;
|
|
unsigned long irq_flags;
|
|
int pc;
|
|
int syscall_nr;
|
|
|
|
syscall_nr = trace_get_syscall_nr(current, regs);
|
|
if (syscall_nr < 0 || syscall_nr >= NR_syscalls)
|
|
return;
|
|
|
|
/* Here we're inside tp handler's rcu_read_lock_sched (__DO_TRACE()) */
|
|
trace_file = rcu_dereference_sched(tr->exit_syscall_files[syscall_nr]);
|
|
if (!trace_file)
|
|
return;
|
|
|
|
if (trace_trigger_soft_disabled(trace_file))
|
|
return;
|
|
|
|
sys_data = syscall_nr_to_meta(syscall_nr);
|
|
if (!sys_data)
|
|
return;
|
|
|
|
local_save_flags(irq_flags);
|
|
pc = preempt_count();
|
|
|
|
buffer = tr->trace_buffer.buffer;
|
|
event = trace_buffer_lock_reserve(buffer,
|
|
sys_data->exit_event->event.type, sizeof(*entry),
|
|
irq_flags, pc);
|
|
if (!event)
|
|
return;
|
|
|
|
entry = ring_buffer_event_data(event);
|
|
entry->nr = syscall_nr;
|
|
entry->ret = syscall_get_return_value(current, regs);
|
|
|
|
event_trigger_unlock_commit(trace_file, buffer, event, entry,
|
|
irq_flags, pc, 0);
|
|
}
|
|
|
|
static int reg_event_syscall_enter(struct trace_event_file *file,
|
|
struct trace_event_call *call)
|
|
{
|
|
struct trace_array *tr = file->tr;
|
|
int ret = 0;
|
|
int num;
|
|
|
|
num = ((struct syscall_metadata *)call->data)->syscall_nr;
|
|
if (WARN_ON_ONCE(num < 0 || num >= NR_syscalls))
|
|
return -ENOSYS;
|
|
mutex_lock(&syscall_trace_lock);
|
|
if (!tr->sys_refcount_enter)
|
|
ret = register_trace_sys_enter(ftrace_syscall_enter, tr);
|
|
if (!ret) {
|
|
rcu_assign_pointer(tr->enter_syscall_files[num], file);
|
|
tr->sys_refcount_enter++;
|
|
}
|
|
mutex_unlock(&syscall_trace_lock);
|
|
return ret;
|
|
}
|
|
|
|
static void unreg_event_syscall_enter(struct trace_event_file *file,
|
|
struct trace_event_call *call)
|
|
{
|
|
struct trace_array *tr = file->tr;
|
|
int num;
|
|
|
|
num = ((struct syscall_metadata *)call->data)->syscall_nr;
|
|
if (WARN_ON_ONCE(num < 0 || num >= NR_syscalls))
|
|
return;
|
|
mutex_lock(&syscall_trace_lock);
|
|
tr->sys_refcount_enter--;
|
|
RCU_INIT_POINTER(tr->enter_syscall_files[num], NULL);
|
|
if (!tr->sys_refcount_enter)
|
|
unregister_trace_sys_enter(ftrace_syscall_enter, tr);
|
|
mutex_unlock(&syscall_trace_lock);
|
|
}
|
|
|
|
static int reg_event_syscall_exit(struct trace_event_file *file,
|
|
struct trace_event_call *call)
|
|
{
|
|
struct trace_array *tr = file->tr;
|
|
int ret = 0;
|
|
int num;
|
|
|
|
num = ((struct syscall_metadata *)call->data)->syscall_nr;
|
|
if (WARN_ON_ONCE(num < 0 || num >= NR_syscalls))
|
|
return -ENOSYS;
|
|
mutex_lock(&syscall_trace_lock);
|
|
if (!tr->sys_refcount_exit)
|
|
ret = register_trace_sys_exit(ftrace_syscall_exit, tr);
|
|
if (!ret) {
|
|
rcu_assign_pointer(tr->exit_syscall_files[num], file);
|
|
tr->sys_refcount_exit++;
|
|
}
|
|
mutex_unlock(&syscall_trace_lock);
|
|
return ret;
|
|
}
|
|
|
|
static void unreg_event_syscall_exit(struct trace_event_file *file,
|
|
struct trace_event_call *call)
|
|
{
|
|
struct trace_array *tr = file->tr;
|
|
int num;
|
|
|
|
num = ((struct syscall_metadata *)call->data)->syscall_nr;
|
|
if (WARN_ON_ONCE(num < 0 || num >= NR_syscalls))
|
|
return;
|
|
mutex_lock(&syscall_trace_lock);
|
|
tr->sys_refcount_exit--;
|
|
RCU_INIT_POINTER(tr->exit_syscall_files[num], NULL);
|
|
if (!tr->sys_refcount_exit)
|
|
unregister_trace_sys_exit(ftrace_syscall_exit, tr);
|
|
mutex_unlock(&syscall_trace_lock);
|
|
}
|
|
|
|
static int __init init_syscall_trace(struct trace_event_call *call)
|
|
{
|
|
int id;
|
|
int num;
|
|
|
|
num = ((struct syscall_metadata *)call->data)->syscall_nr;
|
|
if (num < 0 || num >= NR_syscalls) {
|
|
pr_debug("syscall %s metadata not mapped, disabling ftrace event\n",
|
|
((struct syscall_metadata *)call->data)->name);
|
|
return -ENOSYS;
|
|
}
|
|
|
|
if (set_syscall_print_fmt(call) < 0)
|
|
return -ENOMEM;
|
|
|
|
id = trace_event_raw_init(call);
|
|
|
|
if (id < 0) {
|
|
free_syscall_print_fmt(call);
|
|
return id;
|
|
}
|
|
|
|
return id;
|
|
}
|
|
|
|
struct trace_event_functions enter_syscall_print_funcs = {
|
|
.trace = print_syscall_enter,
|
|
};
|
|
|
|
struct trace_event_functions exit_syscall_print_funcs = {
|
|
.trace = print_syscall_exit,
|
|
};
|
|
|
|
struct trace_event_class __refdata event_class_syscall_enter = {
|
|
.system = "syscalls",
|
|
.reg = syscall_enter_register,
|
|
.define_fields = syscall_enter_define_fields,
|
|
.get_fields = syscall_get_enter_fields,
|
|
.raw_init = init_syscall_trace,
|
|
};
|
|
|
|
struct trace_event_class __refdata event_class_syscall_exit = {
|
|
.system = "syscalls",
|
|
.reg = syscall_exit_register,
|
|
.define_fields = syscall_exit_define_fields,
|
|
.fields = LIST_HEAD_INIT(event_class_syscall_exit.fields),
|
|
.raw_init = init_syscall_trace,
|
|
};
|
|
|
|
unsigned long __init __weak arch_syscall_addr(int nr)
|
|
{
|
|
return (unsigned long)sys_call_table[nr];
|
|
}
|
|
|
|
void __init init_ftrace_syscalls(void)
|
|
{
|
|
struct syscall_metadata *meta;
|
|
unsigned long addr;
|
|
int i;
|
|
|
|
syscalls_metadata = kcalloc(NR_syscalls, sizeof(*syscalls_metadata),
|
|
GFP_KERNEL);
|
|
if (!syscalls_metadata) {
|
|
WARN_ON(1);
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < NR_syscalls; i++) {
|
|
addr = arch_syscall_addr(i);
|
|
meta = find_syscall_meta(addr);
|
|
if (!meta)
|
|
continue;
|
|
|
|
meta->syscall_nr = i;
|
|
syscalls_metadata[i] = meta;
|
|
}
|
|
}
|
|
|
|
#ifdef CONFIG_PERF_EVENTS
|
|
|
|
static DECLARE_BITMAP(enabled_perf_enter_syscalls, NR_syscalls);
|
|
static DECLARE_BITMAP(enabled_perf_exit_syscalls, NR_syscalls);
|
|
static int sys_perf_refcount_enter;
|
|
static int sys_perf_refcount_exit;
|
|
|
|
static void perf_syscall_enter(void *ignore, struct pt_regs *regs, long id)
|
|
{
|
|
struct syscall_metadata *sys_data;
|
|
struct syscall_trace_enter *rec;
|
|
struct hlist_head *head;
|
|
bool valid_prog_array;
|
|
int syscall_nr;
|
|
int rctx;
|
|
int size;
|
|
|
|
syscall_nr = trace_get_syscall_nr(current, regs);
|
|
if (syscall_nr < 0 || syscall_nr >= NR_syscalls)
|
|
return;
|
|
if (!test_bit(syscall_nr, enabled_perf_enter_syscalls))
|
|
return;
|
|
|
|
sys_data = syscall_nr_to_meta(syscall_nr);
|
|
if (!sys_data)
|
|
return;
|
|
|
|
head = this_cpu_ptr(sys_data->enter_event->perf_events);
|
|
valid_prog_array = bpf_prog_array_valid(sys_data->enter_event);
|
|
if (!valid_prog_array && hlist_empty(head))
|
|
return;
|
|
|
|
/* get the size after alignment with the u32 buffer size field */
|
|
size = sizeof(unsigned long) * sys_data->nb_args + sizeof(*rec);
|
|
size = ALIGN(size + sizeof(u32), sizeof(u64));
|
|
size -= sizeof(u32);
|
|
|
|
rec = perf_trace_buf_alloc(size, NULL, &rctx);
|
|
if (!rec)
|
|
return;
|
|
|
|
rec->nr = syscall_nr;
|
|
syscall_get_arguments(current, regs, 0, sys_data->nb_args,
|
|
(unsigned long *)&rec->args);
|
|
perf_trace_buf_submit(rec, size, rctx,
|
|
sys_data->enter_event->event.type, 1, regs,
|
|
head, NULL);
|
|
}
|
|
|
|
static int perf_sysenter_enable(struct trace_event_call *call)
|
|
{
|
|
int ret = 0;
|
|
int num;
|
|
|
|
num = ((struct syscall_metadata *)call->data)->syscall_nr;
|
|
|
|
mutex_lock(&syscall_trace_lock);
|
|
if (!sys_perf_refcount_enter)
|
|
ret = register_trace_sys_enter(perf_syscall_enter, NULL);
|
|
if (ret) {
|
|
pr_info("event trace: Could not activate syscall entry trace point");
|
|
} else {
|
|
set_bit(num, enabled_perf_enter_syscalls);
|
|
sys_perf_refcount_enter++;
|
|
}
|
|
mutex_unlock(&syscall_trace_lock);
|
|
return ret;
|
|
}
|
|
|
|
static void perf_sysenter_disable(struct trace_event_call *call)
|
|
{
|
|
int num;
|
|
|
|
num = ((struct syscall_metadata *)call->data)->syscall_nr;
|
|
|
|
mutex_lock(&syscall_trace_lock);
|
|
sys_perf_refcount_enter--;
|
|
clear_bit(num, enabled_perf_enter_syscalls);
|
|
if (!sys_perf_refcount_enter)
|
|
unregister_trace_sys_enter(perf_syscall_enter, NULL);
|
|
mutex_unlock(&syscall_trace_lock);
|
|
}
|
|
|
|
static void perf_syscall_exit(void *ignore, struct pt_regs *regs, long ret)
|
|
{
|
|
struct syscall_metadata *sys_data;
|
|
struct syscall_trace_exit *rec;
|
|
struct hlist_head *head;
|
|
bool valid_prog_array;
|
|
int syscall_nr;
|
|
int rctx;
|
|
int size;
|
|
|
|
syscall_nr = trace_get_syscall_nr(current, regs);
|
|
if (syscall_nr < 0 || syscall_nr >= NR_syscalls)
|
|
return;
|
|
if (!test_bit(syscall_nr, enabled_perf_exit_syscalls))
|
|
return;
|
|
|
|
sys_data = syscall_nr_to_meta(syscall_nr);
|
|
if (!sys_data)
|
|
return;
|
|
|
|
head = this_cpu_ptr(sys_data->exit_event->perf_events);
|
|
valid_prog_array = bpf_prog_array_valid(sys_data->exit_event);
|
|
if (!valid_prog_array && hlist_empty(head))
|
|
return;
|
|
|
|
/* We can probably do that at build time */
|
|
size = ALIGN(sizeof(*rec) + sizeof(u32), sizeof(u64));
|
|
size -= sizeof(u32);
|
|
|
|
rec = perf_trace_buf_alloc(size, NULL, &rctx);
|
|
if (!rec)
|
|
return;
|
|
|
|
rec->nr = syscall_nr;
|
|
rec->ret = syscall_get_return_value(current, regs);
|
|
perf_trace_buf_submit(rec, size, rctx, sys_data->exit_event->event.type,
|
|
1, regs, head, NULL);
|
|
}
|
|
|
|
static int perf_sysexit_enable(struct trace_event_call *call)
|
|
{
|
|
int ret = 0;
|
|
int num;
|
|
|
|
num = ((struct syscall_metadata *)call->data)->syscall_nr;
|
|
|
|
mutex_lock(&syscall_trace_lock);
|
|
if (!sys_perf_refcount_exit)
|
|
ret = register_trace_sys_exit(perf_syscall_exit, NULL);
|
|
if (ret) {
|
|
pr_info("event trace: Could not activate syscall exit trace point");
|
|
} else {
|
|
set_bit(num, enabled_perf_exit_syscalls);
|
|
sys_perf_refcount_exit++;
|
|
}
|
|
mutex_unlock(&syscall_trace_lock);
|
|
return ret;
|
|
}
|
|
|
|
static void perf_sysexit_disable(struct trace_event_call *call)
|
|
{
|
|
int num;
|
|
|
|
num = ((struct syscall_metadata *)call->data)->syscall_nr;
|
|
|
|
mutex_lock(&syscall_trace_lock);
|
|
sys_perf_refcount_exit--;
|
|
clear_bit(num, enabled_perf_exit_syscalls);
|
|
if (!sys_perf_refcount_exit)
|
|
unregister_trace_sys_exit(perf_syscall_exit, NULL);
|
|
mutex_unlock(&syscall_trace_lock);
|
|
}
|
|
|
|
#endif /* CONFIG_PERF_EVENTS */
|
|
|
|
static int syscall_enter_register(struct trace_event_call *event,
|
|
enum trace_reg type, void *data)
|
|
{
|
|
struct trace_event_file *file = data;
|
|
|
|
switch (type) {
|
|
case TRACE_REG_REGISTER:
|
|
return reg_event_syscall_enter(file, event);
|
|
case TRACE_REG_UNREGISTER:
|
|
unreg_event_syscall_enter(file, event);
|
|
return 0;
|
|
|
|
#ifdef CONFIG_PERF_EVENTS
|
|
case TRACE_REG_PERF_REGISTER:
|
|
return perf_sysenter_enable(event);
|
|
case TRACE_REG_PERF_UNREGISTER:
|
|
perf_sysenter_disable(event);
|
|
return 0;
|
|
case TRACE_REG_PERF_OPEN:
|
|
case TRACE_REG_PERF_CLOSE:
|
|
case TRACE_REG_PERF_ADD:
|
|
case TRACE_REG_PERF_DEL:
|
|
return 0;
|
|
#endif
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int syscall_exit_register(struct trace_event_call *event,
|
|
enum trace_reg type, void *data)
|
|
{
|
|
struct trace_event_file *file = data;
|
|
|
|
switch (type) {
|
|
case TRACE_REG_REGISTER:
|
|
return reg_event_syscall_exit(file, event);
|
|
case TRACE_REG_UNREGISTER:
|
|
unreg_event_syscall_exit(file, event);
|
|
return 0;
|
|
|
|
#ifdef CONFIG_PERF_EVENTS
|
|
case TRACE_REG_PERF_REGISTER:
|
|
return perf_sysexit_enable(event);
|
|
case TRACE_REG_PERF_UNREGISTER:
|
|
perf_sysexit_disable(event);
|
|
return 0;
|
|
case TRACE_REG_PERF_OPEN:
|
|
case TRACE_REG_PERF_CLOSE:
|
|
case TRACE_REG_PERF_ADD:
|
|
case TRACE_REG_PERF_DEL:
|
|
return 0;
|
|
#endif
|
|
}
|
|
return 0;
|
|
}
|