From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1757781AbcAOLWj (ORCPT ); Fri, 15 Jan 2016 06:22:39 -0500 Received: from mga14.intel.com ([192.55.52.115]:7770 "EHLO mga14.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755372AbcAOLWh (ORCPT ); Fri, 15 Jan 2016 06:22:37 -0500 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.22,299,1449561600"; d="scan'208";a="633877982" From: Alexander Shishkin To: Peter Zijlstra Cc: Ingo Molnar , linux-kernel@vger.kernel.org, vince@deater.net, eranian@google.com, Arnaldo Carvalho de Melo , Alexander Shishkin Subject: [PATCH] perf: Cleanup user's child events Date: Fri, 15 Jan 2016 13:22:15 +0200 Message-Id: <1452856935-364-1-git-send-email-alexander.shishkin@linux.intel.com> X-Mailer: git-send-email 2.7.0.rc3 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Events are leaking in the following scenario: user creates an event for task A, task A forks into B (producing a child event), user closes the original event. Both original user's event and its child will remain for as long as task B is around. In other words, we don't clean up children when we try to release the parent. This patch cleans up user event's children when its file descriptor is closed. Signed-off-by: Alexander Shishkin --- kernel/events/core.c | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/kernel/events/core.c b/kernel/events/core.c index 630f53acce..867c4347ea 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -3828,7 +3828,43 @@ EXPORT_SYMBOL_GPL(perf_event_release_kernel); */ static int perf_release(struct inode *inode, struct file *file) { - put_event(file->private_data); + struct perf_event *event = file->private_data, *child, *tmp; + LIST_HEAD(child_list); + + /* + * event::child_mutex nests inside ctx::lock, so move children + * to a safe place first and avoid inversion + */ + mutex_lock(&event->child_mutex); + list_splice_init(&event->child_list, &child_list); + mutex_unlock(&event->child_mutex); + + list_for_each_entry_safe(child, tmp, &child_list, child_list) { + struct perf_event_context *ctx; + + /* + * This is somewhat similar to perf_free_event(), + * except for these events are alive and need + * proper perf_remove_from_context(). + */ + ctx = perf_event_ctx_lock(child); + perf_remove_from_context(child, true); + perf_event_ctx_unlock(child, ctx); + + list_del(&child->child_list); + + /* Children will have exactly one reference */ + free_event(child); + + /* + * This matches the refcount bump in inherit_event(); + * this can't be the last reference. + */ + put_event(event); + } + + /* Must be the last reference */ + put_event(event); return 0; } -- 2.7.0.rc3