From patchwork Tue Sep 23 17:03:07 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pawel Moll X-Patchwork-Id: 37749 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-ee0-f71.google.com (mail-ee0-f71.google.com [74.125.83.71]) by ip-10-151-82-157.ec2.internal (Postfix) with ESMTPS id 7459B20970 for ; Tue, 23 Sep 2014 17:03:55 +0000 (UTC) Received: by mail-ee0-f71.google.com with SMTP id b57sf3116363eek.10 for ; Tue, 23 Sep 2014 10:03:54 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:delivered-to:from:to:cc:subject :date:message-id:in-reply-to:references:sender:precedence:list-id :x-original-sender:x-original-authentication-results:mailing-list :list-post:list-help:list-archive:list-unsubscribe; bh=GtorF2CSliAFGvMckrpQJ8AWKsXrfQEhDeZrNz6+I80=; b=ToX4yKC4KOkdPdvgh4MfeH+pCCW75pl2RgIefXAv6uUUMY/vL4cqkb/apopfa36Ozq i54HMiEKz0L8KLvWv6KmYrKoItntejDJTJSaPYxRjfqplJJvkN5jt2qDWNLWOqVAWaKP 3hL8YbrJnuVNaXEp5MAmDpedacRcO/VVrLHRyK+PnBxUI4J25m5vANJYQC2+c3quMp8P gGjj3Ya0n6oqLZbsVK7qCAuHgvpP00j3DshI3SStXb2YIOcwtzAOOIjIF6+PIu1lwTnj SgWwioX0ZJii7zmlhZRmJW+v+vPBY2khJDFnb3GpEudkIuLapUpYmYPIHCST35KIDYis XVxg== X-Gm-Message-State: ALoCoQkiNo57PDJZq73alsZcIWajNo0emPsem1ybyprBG4gcky8Lq4IMUEIQFKVmLPvKXB08jmNO X-Received: by 10.180.24.72 with SMTP id s8mr941856wif.1.1411491834638; Tue, 23 Sep 2014 10:03:54 -0700 (PDT) MIME-Version: 1.0 X-BeenThere: patchwork-forward@linaro.org Received: by 10.152.7.197 with SMTP id l5ls61800laa.58.gmail; Tue, 23 Sep 2014 10:03:54 -0700 (PDT) X-Received: by 10.152.197.2 with SMTP id iq2mr702463lac.87.1411491834447; Tue, 23 Sep 2014 10:03:54 -0700 (PDT) Received: from mail-la0-f45.google.com (mail-la0-f45.google.com [209.85.215.45]) by mx.google.com with ESMTPS id w4si19436736lag.128.2014.09.23.10.03.53 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Tue, 23 Sep 2014 10:03:53 -0700 (PDT) Received-SPF: pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.215.45 as permitted sender) client-ip=209.85.215.45; Received: by mail-la0-f45.google.com with SMTP id el20so2644113lab.4 for ; Tue, 23 Sep 2014 10:03:53 -0700 (PDT) X-Received: by 10.152.22.137 with SMTP id d9mr845068laf.29.1411491833560; Tue, 23 Sep 2014 10:03:53 -0700 (PDT) X-Forwarded-To: patchwork-forward@linaro.org X-Forwarded-For: patch@linaro.org patchwork-forward@linaro.org Delivered-To: patch@linaro.org Received: by 10.112.130.169 with SMTP id of9csp451988lbb; Tue, 23 Sep 2014 10:03:52 -0700 (PDT) X-Received: by 10.70.129.72 with SMTP id nu8mr1411300pdb.91.1411491831936; Tue, 23 Sep 2014 10:03:51 -0700 (PDT) Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id cd6si22026406pdb.13.2014.09.23.10.03.51 for ; Tue, 23 Sep 2014 10:03:51 -0700 (PDT) Received-SPF: none (google.com: linux-kernel-owner@vger.kernel.org does not designate permitted sender hosts) client-ip=209.132.180.67; Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S932452AbaIWRDd (ORCPT + 27 others); Tue, 23 Sep 2014 13:03:33 -0400 Received: from foss-mx-na.foss.arm.com ([217.140.108.86]:36236 "EHLO foss-mx-na.foss.arm.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S932115AbaIWRDa (ORCPT ); Tue, 23 Sep 2014 13:03:30 -0400 Received: from foss-smtp-na-1.foss.arm.com (unknown [10.80.61.8]) by foss-mx-na.foss.arm.com (Postfix) with ESMTP id AC919104; Tue, 23 Sep 2014 12:03:26 -0500 (CDT) Received: from collaborate-mta1.arm.com (highbank-bc01-b06.austin.arm.com [10.112.81.134]) by foss-smtp-na-1.foss.arm.com (Postfix) with ESMTP id 22C5B5FADE; Tue, 23 Sep 2014 12:03:19 -0500 (CDT) Received: from hornet.Cambridge.Arm.com (hornet.cambridge.arm.com [10.2.201.42]) by collaborate-mta1.arm.com (Postfix) with ESMTP id 9E47913F952; Tue, 23 Sep 2014 12:03:17 -0500 (CDT) From: Pawel Moll To: Richard Cochran , Steven Rostedt , Ingo Molnar , Peter Zijlstra , Paul Mackerras , Arnaldo Carvalho de Melo , John Stultz Cc: linux-kernel@vger.kernel.org, linux-api@vger.kernel.org, Pawel Moll Subject: [PATCH v2 2/2] perf: Userspace event Date: Tue, 23 Sep 2014 18:03:07 +0100 Message-Id: <1411491787-25938-3-git-send-email-pawel.moll@arm.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1411491787-25938-1-git-send-email-pawel.moll@arm.com> References: <1411491787-25938-1-git-send-email-pawel.moll@arm.com> Sender: linux-kernel-owner@vger.kernel.org Precedence: list List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-Removed-Original-Auth: Dkim didn't pass. X-Original-Sender: pawel.moll@arm.com X-Original-Authentication-Results: mx.google.com; spf=pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.215.45 as permitted sender) smtp.mail=patch+caf_=patchwork-forward=linaro.org@linaro.org Mailing-list: list patchwork-forward@linaro.org; contact patchwork-forward+owners@linaro.org X-Google-Group-Id: 836684582541 List-Post: , List-Help: , List-Archive: List-Unsubscribe: , This patch adds a new PERF_COUNT_SW_UEVENT software event and a related PERF_SAMPLE_UEVENT sample. User can now write to the the perf file descriptor, injecting such event in the perf buffer. The UEVENT sample begins with a 32 bit unsigned integer value describing type of the generated event. The type can be set with PERF_EVENT_IOC_SET_UEVENT_TYPE ioctl (zero is the default value). Then follows the 32 bit unsigned size of the data (provided as the "count" argument of the write syscall) and the data itself plus padding aligning the overall sample size to 8 bytes. Data Events with type equal 0 are defined as zero-terminated strings, other types are defined by userspace (the perf tool will contain a list of known values with reference implementation of data content parsers). Possible use cases for this feature: - "perf_printf" like mechanism to add logging messages to one's perf session; in the simplest case it can be just uint32_t type = 0; ioctl(perf_fd, PERF_EVENT_IOC_SET_UEVENT_TYPE, &type); dprintf(perf_fd, "Message"); (note that dprintf does *not* write the terminating '\0'; for users' convenience kernel add it when type is set to zero) - "perf_printf" used by for perf trace tool, where certain traced process' calls are intercepted (eg. using LD_PRELOAD) and treated as logging requests, with it output redirected into the perf buffer - synchronisation of performance data generated in user space with the perf stream coming from the kernel. For example, the marker can be inserted by a JIT engine after it generated portion of the code, but before the code is executed for the first time, allowing the post-processor to pick the correct debugging information. - other example is a system profiling tool taking data from other sources than just perf, which generates a marker at the beginning at at the end of the session (also possibly periodically during the session) to synchronise kernel timestamps with clock values obtained in userspace (gtod or raw_monotonic). Signed-off-by: Pawel Moll --- Changes since v1: - replaced ioctl-based interface with write syscall (there's still a ioctl to set an event type) - replaced all "USERSPACE_EVENT" and alike strings with much shorter "UEVENT" include/linux/perf_event.h | 14 ++++++++ include/uapi/linux/perf_event.h | 24 ++++++++++++- kernel/events/core.c | 80 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 117 insertions(+), 1 deletion(-) diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h index 28b73b2..c130579 100644 --- a/include/linux/perf_event.h +++ b/include/linux/perf_event.h @@ -64,6 +64,12 @@ struct perf_raw_record { void *data; }; +struct perf_uevent { + u32 type; + u32 size; + u8 data[0]; +}; + /* * branch stack layout: * nr: number of taken branches stored in entries[] @@ -433,6 +439,7 @@ struct perf_event { struct pid_namespace *ns; u64 id; + u32 uevent_type; perf_overflow_handler_t overflow_handler; void *overflow_handler_context; @@ -604,6 +611,8 @@ struct perf_sample_data { u64 txn; /* Raw monotonic timestamp, for userspace time correlation */ u64 clock_raw_monotonic; + /* Userspace-originating event */ + struct perf_uevent *uevent; }; static inline void perf_sample_data_init(struct perf_sample_data *data, @@ -685,6 +694,9 @@ perf_sw_event(u32 event_id, u64 nr, struct pt_regs *regs, u64 addr) } } +int perf_uevent_write(struct perf_event *event, u32 type, u32 size, + const char __user *data); + extern struct static_key_deferred perf_sched_events; static inline void perf_event_task_sched_in(struct task_struct *prev, @@ -807,6 +819,8 @@ static inline int perf_event_refresh(struct perf_event *event, int refresh) static inline void perf_sw_event(u32 event_id, u64 nr, struct pt_regs *regs, u64 addr) { } +static inline int perf_uevent_write(struct perf_event *event, u32 type, + u32 size, const char __user *data) { return -EINVAL; } static inline void perf_bp_event(struct perf_event *event, void *data) { } diff --git a/include/uapi/linux/perf_event.h b/include/uapi/linux/perf_event.h index e5a75c5..1fabc2c 100644 --- a/include/uapi/linux/perf_event.h +++ b/include/uapi/linux/perf_event.h @@ -110,6 +110,7 @@ enum perf_sw_ids { PERF_COUNT_SW_ALIGNMENT_FAULTS = 7, PERF_COUNT_SW_EMULATION_FAULTS = 8, PERF_COUNT_SW_DUMMY = 9, + PERF_COUNT_SW_UEVENT = 10, PERF_COUNT_SW_MAX, /* non-ABI */ }; @@ -138,8 +139,9 @@ enum perf_event_sample_format { PERF_SAMPLE_IDENTIFIER = 1U << 16, PERF_SAMPLE_TRANSACTION = 1U << 17, PERF_SAMPLE_CLOCK_RAW_MONOTONIC = 1U << 18, + PERF_SAMPLE_UEVENT = 1U << 19, - PERF_SAMPLE_MAX = 1U << 19, /* non-ABI */ + PERF_SAMPLE_MAX = 1U << 20, /* non-ABI */ }; /* @@ -350,6 +352,7 @@ struct perf_event_attr { #define PERF_EVENT_IOC_SET_OUTPUT _IO ('$', 5) #define PERF_EVENT_IOC_SET_FILTER _IOW('$', 6, char *) #define PERF_EVENT_IOC_ID _IOR('$', 7, __u64 *) +#define PERF_EVENT_IOC_SET_UEVENT_TYPE _IOW('$', 8, __u32) enum perf_event_ioc_flags { PERF_IOC_FLAG_GROUP = 1U << 0, @@ -688,6 +691,25 @@ enum perf_event_type { * { u64 data_src; } && PERF_SAMPLE_DATA_SRC * { u64 transaction; } && PERF_SAMPLE_TRANSACTION * { u64 clock_raw_monotonic; } && PERF_SAMPLE_CLOCK_RAW_MONOTONIC + * + * # + * # Contents of UEVENT sample data depend on its type. + * # + * # Type 0 means that the data is a zero-terminated string that + * # can be printf-ed in the normal way. + * # + * # Meaning of other type values depends on the userspace + * # and the perf tool code contains a list of those with + * # reference implementations of parsers. + * # + * # Overall size of the sample (including type and size fields) + * # is always aligned to 8 bytes by adding padding after + * # the data. + * # + * { u32 type; + * u32 size; + * char data[size]; + * char __padding[] } && PERF_SAMPLE_UEVENT * }; */ PERF_RECORD_SAMPLE = 9, diff --git a/kernel/events/core.c b/kernel/events/core.c index f6df547..69ca8c9 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -3526,6 +3526,15 @@ perf_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) return perf_read_hw(event, buf, count); } +static ssize_t +perf_write(struct file *file, const char __user *buf, size_t count, + loff_t *ppos) +{ + struct perf_event *event = file->private_data; + + return perf_uevent_write(event, event->uevent_type, count, buf); +} + static unsigned int perf_poll(struct file *file, poll_table *wait) { struct perf_event *event = file->private_data; @@ -3636,6 +3645,17 @@ unlock: return ret; } +static int perf_event_set_uevent_type(struct perf_event *event, u32 __user *arg) +{ + if (!arg) + return -EINVAL; + + if (copy_from_user(&event->uevent_type, arg, sizeof(*arg))) + return -EFAULT; + + return 0; +} + static const struct file_operations perf_fops; static inline int perf_fget_light(int fd, struct fd *p) @@ -3709,6 +3729,9 @@ static long perf_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case PERF_EVENT_IOC_SET_FILTER: return perf_event_set_filter(event, (void __user *)arg); + case PERF_EVENT_IOC_SET_UEVENT_TYPE: + return perf_event_set_uevent_type(event, (u32 __user *)arg); + default: return -ENOTTY; } @@ -4244,6 +4267,7 @@ static const struct file_operations perf_fops = { .llseek = no_llseek, .release = perf_release, .read = perf_read, + .write = perf_write, .poll = perf_poll, .unlocked_ioctl = perf_ioctl, .compat_ioctl = perf_compat_ioctl, @@ -4727,6 +4751,16 @@ void perf_output_sample(struct perf_output_handle *handle, if (sample_type & PERF_SAMPLE_CLOCK_RAW_MONOTONIC) perf_output_put(handle, data->clock_raw_monotonic); + if (sample_type & PERF_SAMPLE_UEVENT) { + int size = data->uevent->size; + int padding = ALIGN(size, sizeof(u64)) - size; + + perf_output_put(handle, data->uevent->type); + perf_output_put(handle, size); + __output_copy(handle, data->uevent->data, size); + perf_output_skip(handle, padding); + }; + if (!event->attr.watermark) { int wakeup_events = event->attr.wakeup_events; @@ -4834,6 +4868,10 @@ void perf_prepare_sample(struct perf_event_header *header, data->stack_user_size = stack_size; header->size += size; } + + if (sample_type & PERF_SAMPLE_UEVENT) + header->size += sizeof(u32) + sizeof(u32) + + ALIGN(data->uevent->size, sizeof(u64)); } static void perf_event_output(struct perf_event *event, @@ -5961,6 +5999,48 @@ static struct pmu perf_swevent = { .event_idx = perf_swevent_event_idx, }; +int perf_uevent_write(struct perf_event *event, u32 type, u32 size, + const char __user *data) +{ + struct perf_uevent *uevent; + struct perf_sample_data sample; + struct pt_regs *regs = current_pt_regs(); + + /* Need some sane limit */ + if (size > PAGE_SIZE) + return -EFBIG; + + /* + * Type 0 means zero-terminated string, but standard dprintf() + * doesn't write the zero character. Let's allocate one more byte + * for such event... + */ + uevent = kmalloc(sizeof(*uevent) + size + (type == 0 ? 1 : 0), + GFP_KERNEL); + if (!uevent) + return -ENOMEM; + + if (copy_from_user(uevent->data, data, size)) { + kfree(uevent); + return -EFAULT; + } + + /* ... and then zero it, if necessary. */ + if (type == 0 && uevent->data[size - 1]) + uevent->data[size++] = '\0'; + + uevent->type = type; + uevent->size = size; + + perf_sample_data_init(&sample, 0, 0); + sample.uevent = uevent; + perf_event_output(event, &sample, regs); + + kfree(uevent); + + return size; +} + #ifdef CONFIG_EVENT_TRACING static int perf_tp_filter_match(struct perf_event *event,