|
@@ -48,6 +48,8 @@
|
|
|
#include <linux/parser.h>
|
|
|
#include <linux/sched/clock.h>
|
|
|
#include <linux/sched/mm.h>
|
|
|
+#include <linux/proc_ns.h>
|
|
|
+#include <linux/mount.h>
|
|
|
|
|
|
#include "internal.h"
|
|
|
|
|
@@ -379,6 +381,7 @@ static DEFINE_PER_CPU(struct pmu_event_list, pmu_sb_events);
|
|
|
|
|
|
static atomic_t nr_mmap_events __read_mostly;
|
|
|
static atomic_t nr_comm_events __read_mostly;
|
|
|
+static atomic_t nr_namespaces_events __read_mostly;
|
|
|
static atomic_t nr_task_events __read_mostly;
|
|
|
static atomic_t nr_freq_events __read_mostly;
|
|
|
static atomic_t nr_switch_events __read_mostly;
|
|
@@ -3991,6 +3994,8 @@ static void unaccount_event(struct perf_event *event)
|
|
|
atomic_dec(&nr_mmap_events);
|
|
|
if (event->attr.comm)
|
|
|
atomic_dec(&nr_comm_events);
|
|
|
+ if (event->attr.namespaces)
|
|
|
+ atomic_dec(&nr_namespaces_events);
|
|
|
if (event->attr.task)
|
|
|
atomic_dec(&nr_task_events);
|
|
|
if (event->attr.freq)
|
|
@@ -6491,6 +6496,7 @@ static void perf_event_task(struct task_struct *task,
|
|
|
void perf_event_fork(struct task_struct *task)
|
|
|
{
|
|
|
perf_event_task(task, NULL, 1);
|
|
|
+ perf_event_namespaces(task);
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -6592,6 +6598,132 @@ void perf_event_comm(struct task_struct *task, bool exec)
|
|
|
perf_event_comm_event(&comm_event);
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * namespaces tracking
|
|
|
+ */
|
|
|
+
|
|
|
+struct perf_namespaces_event {
|
|
|
+ struct task_struct *task;
|
|
|
+
|
|
|
+ struct {
|
|
|
+ struct perf_event_header header;
|
|
|
+
|
|
|
+ u32 pid;
|
|
|
+ u32 tid;
|
|
|
+ u64 nr_namespaces;
|
|
|
+ struct perf_ns_link_info link_info[NR_NAMESPACES];
|
|
|
+ } event_id;
|
|
|
+};
|
|
|
+
|
|
|
+static int perf_event_namespaces_match(struct perf_event *event)
|
|
|
+{
|
|
|
+ return event->attr.namespaces;
|
|
|
+}
|
|
|
+
|
|
|
+static void perf_event_namespaces_output(struct perf_event *event,
|
|
|
+ void *data)
|
|
|
+{
|
|
|
+ struct perf_namespaces_event *namespaces_event = data;
|
|
|
+ struct perf_output_handle handle;
|
|
|
+ struct perf_sample_data sample;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ if (!perf_event_namespaces_match(event))
|
|
|
+ return;
|
|
|
+
|
|
|
+ perf_event_header__init_id(&namespaces_event->event_id.header,
|
|
|
+ &sample, event);
|
|
|
+ ret = perf_output_begin(&handle, event,
|
|
|
+ namespaces_event->event_id.header.size);
|
|
|
+ if (ret)
|
|
|
+ return;
|
|
|
+
|
|
|
+ namespaces_event->event_id.pid = perf_event_pid(event,
|
|
|
+ namespaces_event->task);
|
|
|
+ namespaces_event->event_id.tid = perf_event_tid(event,
|
|
|
+ namespaces_event->task);
|
|
|
+
|
|
|
+ perf_output_put(&handle, namespaces_event->event_id);
|
|
|
+
|
|
|
+ perf_event__output_id_sample(event, &handle, &sample);
|
|
|
+
|
|
|
+ perf_output_end(&handle);
|
|
|
+}
|
|
|
+
|
|
|
+static void perf_fill_ns_link_info(struct perf_ns_link_info *ns_link_info,
|
|
|
+ struct task_struct *task,
|
|
|
+ const struct proc_ns_operations *ns_ops)
|
|
|
+{
|
|
|
+ struct path ns_path;
|
|
|
+ struct inode *ns_inode;
|
|
|
+ void *error;
|
|
|
+
|
|
|
+ error = ns_get_path(&ns_path, task, ns_ops);
|
|
|
+ if (!error) {
|
|
|
+ ns_inode = ns_path.dentry->d_inode;
|
|
|
+ ns_link_info->dev = new_encode_dev(ns_inode->i_sb->s_dev);
|
|
|
+ ns_link_info->ino = ns_inode->i_ino;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void perf_event_namespaces(struct task_struct *task)
|
|
|
+{
|
|
|
+ struct perf_namespaces_event namespaces_event;
|
|
|
+ struct perf_ns_link_info *ns_link_info;
|
|
|
+
|
|
|
+ if (!atomic_read(&nr_namespaces_events))
|
|
|
+ return;
|
|
|
+
|
|
|
+ namespaces_event = (struct perf_namespaces_event){
|
|
|
+ .task = task,
|
|
|
+ .event_id = {
|
|
|
+ .header = {
|
|
|
+ .type = PERF_RECORD_NAMESPACES,
|
|
|
+ .misc = 0,
|
|
|
+ .size = sizeof(namespaces_event.event_id),
|
|
|
+ },
|
|
|
+ /* .pid */
|
|
|
+ /* .tid */
|
|
|
+ .nr_namespaces = NR_NAMESPACES,
|
|
|
+ /* .link_info[NR_NAMESPACES] */
|
|
|
+ },
|
|
|
+ };
|
|
|
+
|
|
|
+ ns_link_info = namespaces_event.event_id.link_info;
|
|
|
+
|
|
|
+ perf_fill_ns_link_info(&ns_link_info[MNT_NS_INDEX],
|
|
|
+ task, &mntns_operations);
|
|
|
+
|
|
|
+#ifdef CONFIG_USER_NS
|
|
|
+ perf_fill_ns_link_info(&ns_link_info[USER_NS_INDEX],
|
|
|
+ task, &userns_operations);
|
|
|
+#endif
|
|
|
+#ifdef CONFIG_NET_NS
|
|
|
+ perf_fill_ns_link_info(&ns_link_info[NET_NS_INDEX],
|
|
|
+ task, &netns_operations);
|
|
|
+#endif
|
|
|
+#ifdef CONFIG_UTS_NS
|
|
|
+ perf_fill_ns_link_info(&ns_link_info[UTS_NS_INDEX],
|
|
|
+ task, &utsns_operations);
|
|
|
+#endif
|
|
|
+#ifdef CONFIG_IPC_NS
|
|
|
+ perf_fill_ns_link_info(&ns_link_info[IPC_NS_INDEX],
|
|
|
+ task, &ipcns_operations);
|
|
|
+#endif
|
|
|
+#ifdef CONFIG_PID_NS
|
|
|
+ perf_fill_ns_link_info(&ns_link_info[PID_NS_INDEX],
|
|
|
+ task, &pidns_operations);
|
|
|
+#endif
|
|
|
+#ifdef CONFIG_CGROUPS
|
|
|
+ perf_fill_ns_link_info(&ns_link_info[CGROUP_NS_INDEX],
|
|
|
+ task, &cgroupns_operations);
|
|
|
+#endif
|
|
|
+
|
|
|
+ perf_iterate_sb(perf_event_namespaces_output,
|
|
|
+ &namespaces_event,
|
|
|
+ NULL);
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* mmap tracking
|
|
|
*/
|
|
@@ -9146,6 +9278,8 @@ static void account_event(struct perf_event *event)
|
|
|
atomic_inc(&nr_mmap_events);
|
|
|
if (event->attr.comm)
|
|
|
atomic_inc(&nr_comm_events);
|
|
|
+ if (event->attr.namespaces)
|
|
|
+ atomic_inc(&nr_namespaces_events);
|
|
|
if (event->attr.task)
|
|
|
atomic_inc(&nr_task_events);
|
|
|
if (event->attr.freq)
|
|
@@ -9691,6 +9825,11 @@ SYSCALL_DEFINE5(perf_event_open,
|
|
|
return -EACCES;
|
|
|
}
|
|
|
|
|
|
+ if (attr.namespaces) {
|
|
|
+ if (!capable(CAP_SYS_ADMIN))
|
|
|
+ return -EACCES;
|
|
|
+ }
|
|
|
+
|
|
|
if (attr.freq) {
|
|
|
if (attr.sample_freq > sysctl_perf_event_sample_rate)
|
|
|
return -EINVAL;
|