From: Alexander Shishkin <alexander.shishkin@linux.intel.com>
To: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Ingo Molnar <mingo@redhat.com>,
linux-kernel@vger.kernel.org, acme@redhat.com,
kirill.shutemov@linux.intel.com, Borislav Petkov <bp@alien8.de>,
rric@kernel.org,
Alexander Shishkin <alexander.shishkin@linux.intel.com>
Subject: [RFC PATCH 11/17] perf: Implement mlock accounting for shmem ring buffers
Date: Tue, 5 Sep 2017 16:30:20 +0300 [thread overview]
Message-ID: <20170905133026.13689-12-alexander.shishkin@linux.intel.com> (raw)
In-Reply-To: <20170905133026.13689-1-alexander.shishkin@linux.intel.com>
With shmem ring buffers, one can mlock at most nr_pages * nr_cpus, we
only need to do the accounting once the event is created (by means of
sys_perf_event_open()). This implements such accounting, by adding a
shared reference counter: when it goes 0 -> 1, we account the pages,
when it goes to 0, we undo the accounting.
Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
---
kernel/events/core.c | 12 +++--
kernel/events/internal.h | 5 +-
kernel/events/ring_buffer.c | 124 +++++++++++++++++++++++++++++++++++++-------
3 files changed, 116 insertions(+), 25 deletions(-)
diff --git a/kernel/events/core.c b/kernel/events/core.c
index c80ffcdb5c..1fed69d4ba 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -4309,7 +4309,6 @@ static void _free_event(struct perf_event *event)
event->attach_state &= ~PERF_ATTACH_DETACHED;
- ring_buffer_unaccount(event->rb, false);
rb_free_detached(event->rb, event);
}
@@ -9525,9 +9524,11 @@ static void account_event(struct perf_event *event)
account_pmu_sb_event(event);
}
-static int perf_event_detach(struct perf_event *event, struct task_struct *task,
- struct mm_struct *mm)
+static int
+perf_event_detach(struct perf_event *event, struct perf_event *parent_event,
+ struct task_struct *task, struct mm_struct *mm)
{
+ struct ring_buffer *parent_rb = parent_event ? parent_event->rb : NULL;
char *filename;
int err;
@@ -9545,7 +9546,7 @@ static int perf_event_detach(struct perf_event *event, struct task_struct *task,
if (!event->dent)
return -ENOMEM;
- err = rb_alloc_detached(event, task, mm);
+ err = rb_alloc_detached(event, task, mm, parent_rb);
if (err) {
tracefs_remove(event->dent);
event->dent = NULL;
@@ -11015,7 +11016,8 @@ inherit_event(struct perf_event *parent_event,
if (detached) {
int err;
- err = perf_event_detach(child_event, child, NULL);
+ err = perf_event_detach(child_event, parent_event, child,
+ NULL);
if (err) {
perf_free_event(child_event, child_ctx);
mutex_unlock(&parent_event->child_mutex);
diff --git a/kernel/events/internal.h b/kernel/events/internal.h
index 80d36a7277..3dc66961d9 100644
--- a/kernel/events/internal.h
+++ b/kernel/events/internal.h
@@ -53,6 +53,8 @@ struct ring_buffer {
void **aux_pages;
void *aux_priv;
+ atomic_t *acct_refcount;
+
/* tmpfs file for kernel-owned ring buffers */
struct file *shmem_file;
unsigned long shmem_file_addr;
@@ -93,7 +95,8 @@ extern int rb_alloc_aux(struct ring_buffer *rb, struct perf_event *event,
extern void rb_free_aux(struct ring_buffer *rb);
extern int rb_alloc_detached(struct perf_event *event,
struct task_struct *task,
- struct mm_struct *mm);
+ struct mm_struct *mm,
+ struct ring_buffer *parent_rb);
extern void rb_free_detached(struct ring_buffer *rb, struct perf_event *event);
extern struct ring_buffer *ring_buffer_get(struct perf_event *event);
extern void ring_buffer_put(struct ring_buffer *rb);
diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c
index 771dfdb71f..896d441642 100644
--- a/kernel/events/ring_buffer.c
+++ b/kernel/events/ring_buffer.c
@@ -570,8 +570,8 @@ void *perf_get_aux(struct perf_output_handle *handle)
* error out. Otherwise, keep track of the pages used in the ring_buffer so
* that the accounting can be undone when the pages are freed.
*/
-static int ring_buffer_account(struct ring_buffer *rb, struct mm_struct *mm,
- unsigned long nr_pages, bool aux)
+static int __ring_buffer_account(struct ring_buffer *rb, struct mm_struct *mm,
+ unsigned long nr_pages, unsigned long *locked)
{
unsigned long total, limit, pinned;
@@ -589,6 +589,9 @@ static int ring_buffer_account(struct ring_buffer *rb, struct mm_struct *mm,
total = atomic_long_read(&rb->mmap_user->locked_vm) + nr_pages;
+ free_uid(rb->mmap_user);
+ rb->mmap_user = NULL;
+
pinned = 0;
if (total > limit) {
/*
@@ -609,27 +612,33 @@ static int ring_buffer_account(struct ring_buffer *rb, struct mm_struct *mm,
return -EPERM;
}
- if (aux)
- rb->aux_mmap_locked = pinned;
- else
- rb->mmap_locked = pinned;
-
+ *locked = pinned;
mm->pinned_vm += pinned;
}
if (!rb->mmap_mapping)
rb->mmap_mapping = mm;
- /* account for user page */
- if (!aux)
- nr_pages++;
-
rb->mmap_user = get_current_user();
atomic_long_add(nr_pages, &rb->mmap_user->locked_vm);
return 0;
}
+static int ring_buffer_account(struct ring_buffer *rb, struct mm_struct *mm,
+ unsigned long nr_pages, bool aux)
+{
+ int ret;
+
+ /* account for user page */
+ if (!aux)
+ nr_pages++;
+ ret = __ring_buffer_account(rb, mm, nr_pages,
+ aux ? &rb->aux_mmap_locked : &rb->mmap_locked);
+
+ return ret;
+}
+
/*
* Undo the mlock pages accounting done in ring_buffer_account().
*/
@@ -641,6 +650,9 @@ void ring_buffer_unaccount(struct ring_buffer *rb, bool aux)
if (!rb->nr_pages && !rb->aux_nr_pages)
return;
+ if (WARN_ON_ONCE(!rb->mmap_user))
+ return;
+
atomic_long_sub(nr_pages, &rb->mmap_user->locked_vm);
if (rb->mmap_mapping)
rb->mmap_mapping->pinned_vm -= pinned;
@@ -850,7 +862,8 @@ int rb_alloc_aux(struct ring_buffer *rb, struct perf_event *event,
void rb_free_aux(struct ring_buffer *rb)
{
if (atomic_dec_and_test(&rb->aux_refcount)) {
- ring_buffer_unaccount(rb, true);
+ if (!rb->shmem_file)
+ ring_buffer_unaccount(rb, true);
__rb_free_aux(rb);
}
@@ -1087,13 +1100,68 @@ void rb_put_kernel_pages(struct ring_buffer *rb, bool final)
WRITE_ONCE(rb->shmem_pages_in, 0);
}
+/*
+ * SHMEM memory is accounted once per user allocated event (via
+ * the syscall), since we can have at most NR_CPUS * nr_pages
+ * pinned pages at any given point in time, regardless of how
+ * many events there actually are.
+ *
+ * The first one (parent_rb==NULL) is where we do the accounting;
+ * it will also be the one coming from the syscall, so if it fails,
+ * we'll hand them back the error.
+ * Others just inherit and bump the counter; can't fail.
+ */
+static int
+rb_shmem_account(struct ring_buffer *rb, struct ring_buffer *parent_rb)
+{
+ unsigned long nr_pages = perf_rb_size(rb) >> PAGE_SHIFT;
+ int ret = 0;
+
+ if (parent_rb) {
+ /* "parent" rb *must* have accounting refcounter */
+ if (WARN_ON_ONCE(!parent_rb->acct_refcount))
+ return -EINVAL;
+
+ rb->acct_refcount = parent_rb->acct_refcount;
+ atomic_inc(rb->acct_refcount);
+
+ return 0;
+ }
+
+ /* All (data + aux + user page) in one go */
+ ret = __ring_buffer_account(rb, NULL, nr_pages,
+ &rb->mmap_locked);
+ if (ret)
+ return ret;
+
+ rb->acct_refcount = kmalloc(sizeof(*rb->acct_refcount),
+ GFP_KERNEL);
+ if (!rb->acct_refcount)
+ return -ENOMEM;
+
+ atomic_set(rb->acct_refcount, 1);
+
+ return 0;
+}
+
+static void rb_shmem_unaccount(struct ring_buffer *rb)
+{
+ if (!atomic_dec_and_test(rb->acct_refcount)) {
+ rb->acct_refcount = NULL;
+ return;
+ }
+
+ ring_buffer_unaccount(rb, false);
+ kfree(rb->acct_refcount);
+}
+
/*
* Allocate a ring_buffer for a detached event and attach it to this event.
* There's one ring_buffer per detached event and vice versa, so
* ring_buffer_attach() does not apply.
*/
int rb_alloc_detached(struct perf_event *event, struct task_struct *task,
- struct mm_struct *mm)
+ struct mm_struct *mm, struct ring_buffer *parent_rb)
{
int aux_nr_pages = event->attr.detached_aux_nr_pages;
int nr_pages = event->attr.detached_nr_pages;
@@ -1116,18 +1184,22 @@ int rb_alloc_detached(struct perf_event *event, struct task_struct *task,
if (IS_ERR(rb))
return PTR_ERR(rb);
+ if (flags & RING_BUFFER_SHMEM) {
+ ret = rb_shmem_account(rb, parent_rb);
+ if (ret)
+ goto err_free;
+ }
+
if (aux_nr_pages) {
ret = rb_alloc_aux(rb, event, pgoff, aux_nr_pages, 0, flags);
if (ret)
- goto err_free;
+ goto err_unaccount;
}
if (flags & RING_BUFFER_SHMEM) {
ret = rb_shmem_setup(event, task, rb);
- if (ret) {
- rb_free_aux(rb);
- goto err_free;
- }
+ if (ret)
+ goto err_free_aux;
rb_toggle_paused(rb, true);
} else {
@@ -1146,8 +1218,19 @@ int rb_alloc_detached(struct perf_event *event, struct task_struct *task,
return 0;
+err_free_aux:
+ if (!(flags & RING_BUFFER_SHMEM))
+ rb_free_aux(rb);
+
+err_unaccount:
+ if (flags & RING_BUFFER_SHMEM)
+ rb_shmem_unaccount(rb);
+
err_free:
- rb_free(rb);
+ if (flags & RING_BUFFER_SHMEM)
+ kfree(rb);
+ else
+ rb_free(rb);
return ret;
}
@@ -1161,6 +1244,9 @@ void rb_free_detached(struct ring_buffer *rb, struct perf_event *event)
rb_shmem_unmap(event);
shmem_truncate_range(rb->shmem_file->f_inode, 0, (loff_t)-1);
rb_put_kernel_pages(rb, true);
+ rb_shmem_unaccount(rb);
+ } else {
+ ring_buffer_unaccount(rb, false);
}
atomic_set(&rb->aux_mmap_count, 0);
--
2.14.1
next prev parent reply other threads:[~2017-09-05 13:44 UTC|newest]
Thread overview: 34+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-09-05 13:30 [RFC PATCH 00/17] perf: Detached events Alexander Shishkin
2017-09-05 13:30 ` [RFC PATCH 01/17] perf: Allow mmapping only user page Alexander Shishkin
2017-09-06 16:28 ` Borislav Petkov
2017-09-13 11:35 ` Alexander Shishkin
2017-09-13 12:58 ` Borislav Petkov
2017-09-05 13:30 ` [RFC PATCH 02/17] perf: Factor out mlock accounting Alexander Shishkin
2017-09-05 13:30 ` [RFC PATCH 03/17] tracefs: De-globalize instances' callbacks Alexander Shishkin
2018-01-24 18:54 ` Steven Rostedt
2017-09-05 13:30 ` [RFC PATCH 04/17] tracefs: Add ->unlink callback to tracefs_dir_ops Alexander Shishkin
2017-09-05 13:30 ` [RFC PATCH 05/17] perf: Introduce detached events Alexander Shishkin
2017-10-03 14:34 ` Peter Zijlstra
2017-10-06 11:23 ` Alexander Shishkin
2017-09-05 13:30 ` [RFC PATCH 06/17] perf: Add buffers to the " Alexander Shishkin
2017-10-03 14:36 ` Peter Zijlstra
2017-09-05 13:30 ` [RFC PATCH 07/17] perf: Add pmu_info to user page Alexander Shishkin
2017-10-03 14:40 ` Peter Zijlstra
2017-09-05 13:30 ` [RFC PATCH 08/17] perf: Allow inheritance for detached events Alexander Shishkin
2017-10-03 14:42 ` Peter Zijlstra
2017-10-06 11:40 ` Alexander Shishkin
2017-09-05 13:30 ` [RFC PATCH 09/17] perf: Use shmemfs pages for userspace-only per-thread " Alexander Shishkin
2017-10-03 14:43 ` Peter Zijlstra
2017-10-06 11:52 ` Alexander Shishkin
2017-09-05 13:30 ` [RFC PATCH 10/17] perf: Implement pinning and scheduling for SHMEM events Alexander Shishkin
2017-09-05 13:30 ` Alexander Shishkin [this message]
2017-09-05 13:30 ` [RFC PATCH 12/17] perf: Track pinned events per user Alexander Shishkin
2017-09-05 13:30 ` [RFC PATCH 13/17] perf: Re-inject shmem buffers after exec Alexander Shishkin
2017-09-05 13:30 ` [RFC PATCH 14/17] perf: Add ioctl(REATTACH) for detached events Alexander Shishkin
2017-10-03 14:50 ` Peter Zijlstra
2017-09-05 13:30 ` [RFC PATCH 15/17] perf: Allow controlled non-root access to " Alexander Shishkin
2017-10-03 14:53 ` Peter Zijlstra
2017-09-05 13:30 ` [RFC PATCH 16/17] perf/x86/intel/pt: Add PMU info Alexander Shishkin
2017-09-05 13:30 ` [RFC PATCH 17/17] perf/x86/intel/bts: " Alexander Shishkin
2017-09-06 16:24 ` [RFC PATCH 00/17] perf: Detached events Borislav Petkov
2017-09-13 11:54 ` Alexander Shishkin
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20170905133026.13689-12-alexander.shishkin@linux.intel.com \
--to=alexander.shishkin@linux.intel.com \
--cc=a.p.zijlstra@chello.nl \
--cc=acme@redhat.com \
--cc=bp@alien8.de \
--cc=kirill.shutemov@linux.intel.com \
--cc=linux-kernel@vger.kernel.org \
--cc=mingo@redhat.com \
--cc=rric@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®