Received: by 2002:a25:7ec1:0:0:0:0:0 with SMTP id z184csp6898980ybc; Thu, 28 Nov 2019 07:20:11 -0800 (PST) X-Google-Smtp-Source: APXvYqyVE9AfIP2M6UtQa2IkkAo8FMGGuUERCvbZ5DIUol5mLTJPkAT+94g8l/g4lM6gnqmpyybO X-Received: by 2002:a17:906:2893:: with SMTP id o19mr56598825ejd.32.1574954411470; Thu, 28 Nov 2019 07:20:11 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1574954411; cv=none; d=google.com; s=arc-20160816; b=fUX75T5s/QrGoTbRD4MVlYk3Z96UKeZYpAFUn/zVzby49tb0g/vWAwIA6eob39oeKD 21JTzui992gN6WS8s8jili8hwWdhxzRx21rFbAR+iD5v3N5ulVH4ovn5TWI1Anw2a6sr mrpYZu7/WGhKrvw1OIYOiai+ph4hbJulCWfYHsFgj2/A4DGLhJPBbkY4LafwhbtG+wg9 3uLxTRMn5n7rSg5tVoac0yRoBgzSx7+fZ2hXcST1XQcARbMZmxliGQ+1zMcNoef03f7P +tGJZqhTFCM9kPEapkEPoSksPnoEkjOUXmpP+RMrV6e/h4XfnLytkpNKwqn8nPodcEbJ L8Gw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from; bh=xhPWUgKf7k/ihY7EkCmZX6c/L200YgZw+FX57ote9tM=; b=PnPsnZ6PACYb5t9zTwVe15Y3Zv0Q80YBvcoQvI5FhbkwfdJRYWHM5N8Pvl/OMPvfZ6 rwPTRXOAvZizhPOQh8r6xGOTHSq4TUuF93oR67/MeewwJ3klMAohs6Ym/eQyYXPcSNUd lI0MWc4dmSzD/4bReO451VNMac8kBqlns23hp8mTeTqSVRuCUqkC+n8IB9Q+sL6RroxM DFRqVlCc9H3KcCsvBJBV7E0Kz6mpCnhykZ9l3lBkIYWU9PKHpOfRbV3PgeRDmdXpDUFe sgCa+8K5rKmGP1dY8iWvUaUBcoNTM74k2y8dPs+isZvud8Mk+bw5HPj2nc/QNwx7nFPO FJYg== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=intel.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id l19si12676739ejx.406.2019.11.28.07.19.47; Thu, 28 Nov 2019 07:20:11 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=intel.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726758AbfK1PR2 (ORCPT + 99 others); Thu, 28 Nov 2019 10:17:28 -0500 Received: from mga12.intel.com ([192.55.52.136]:39027 "EHLO mga12.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726436AbfK1PR1 (ORCPT ); Thu, 28 Nov 2019 10:17:27 -0500 X-Amp-Result: SKIPPED(no attachment in message) X-Amp-File-Uploaded: False Received: from orsmga007.jf.intel.com ([10.7.209.58]) by fmsmga106.fm.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 28 Nov 2019 07:17:27 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.69,253,1571727600"; d="scan'208";a="199551673" Received: from otc-lr-04.jf.intel.com ([10.54.39.104]) by orsmga007.jf.intel.com with ESMTP; 28 Nov 2019 07:17:26 -0800 From: kan.liang@linux.intel.com To: peterz@infradead.org, mingo@redhat.com, acme@kernel.org, tglx@linutronix.de, bp@alien8.de, linux-kernel@vger.kernel.org Cc: eranian@google.com, alexey.budankov@linux.intel.com, vitaly.slobodskoy@intel.com, ak@linux.intel.com, Kan Liang Subject: [RFC PATCH 2/8] perf: Helpers for alloc/init/fini PMU specific data Date: Thu, 28 Nov 2019 07:14:25 -0800 Message-Id: <1574954071-6321-2-git-send-email-kan.liang@linux.intel.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1574954071-6321-1-git-send-email-kan.liang@linux.intel.com> References: <1574954071-6321-1-git-send-email-kan.liang@linux.intel.com> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Kan Liang The PMU specific data for the monitored tasks is allocated/initialized/freed during LBR call stack monitoring. Several helper functions are provided. alloc_task_ctx_data_rcu() is used to allocate the perf_ctx_data for a task when RCU protected perf_ctx_data is NULL. It doesn't update the refcount if the perf_ctx_data has already been allocated. init_task_ctx_data_rcu() is similar as alloc_task_ctx_data_rcu(). But it updates the refcount if the perf_ctx_data was already allocated. fini_task_ctx_data_rcu() is to free the RCU protected perf_ctx_data when there are no users, or force flag is set. A lock is required for these functions, which is used to sync the writers of perf_ctx_data RCU pointer and refcount. The functions will be used by the following patch. Reviewed-by: Alexey Budankov Signed-off-by: Kan Liang --- kernel/events/core.c | 154 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) diff --git a/kernel/events/core.c b/kernel/events/core.c index 43567d1..b4976e0 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -4440,6 +4440,160 @@ static void unaccount_freq_event(void) atomic_dec(&nr_freq_events); } +static int +alloc_perf_ctx_data(size_t ctx_size, gfp_t flags, + struct perf_ctx_data **task_ctx_data) +{ + struct perf_ctx_data *ctx_data; + + ctx_data = kzalloc(sizeof(struct perf_ctx_data), flags); + if (!ctx_data) + return -ENOMEM; + + ctx_data->data = kzalloc(ctx_size, flags); + if (!ctx_data->data) { + kfree(ctx_data); + return -ENOMEM; + } + + ctx_data->data_size = ctx_size; + *task_ctx_data = ctx_data; + + return 0; +} + +static int +__alloc_task_ctx_data_rcu(struct task_struct *task, + size_t ctx_size, gfp_t flags) +{ + struct perf_ctx_data *ctx_data = task->perf_ctx_data; + int ret; + + lockdep_assert_held_once(&task->perf_ctx_data_lock); + + ret = alloc_perf_ctx_data(ctx_size, flags, &ctx_data); + if (ret) + return ret; + + ctx_data->refcount = 1; + + rcu_assign_pointer(task->perf_ctx_data, ctx_data); + + return 0; +} + +/** + * alloc perf_ctx_data for a task + * @task: Target Task + * @ctx_size: Size of PMU specific data + * @flags: Allocation flags + * + * Allocate perf_ctx_data and update the RCU pointer. + * If the perf_ctx_data has been allocated, return 0. + * Lock is required to sync the writers of perf_ctx_data RCU pointer + * and refcount. + */ +static int +alloc_task_ctx_data_rcu(struct task_struct *task, + size_t ctx_size, gfp_t flags) +{ + unsigned long lock_flags; + int ret = 0; + + raw_spin_lock_irqsave(&task->perf_ctx_data_lock, lock_flags); + + if (task->perf_ctx_data) + goto unlock; + + ret = __alloc_task_ctx_data_rcu(task, ctx_size, flags); + +unlock: + raw_spin_unlock_irqrestore(&task->perf_ctx_data_lock, lock_flags); + + return ret; +} + +static int +__init_task_ctx_data_rcu(struct task_struct *task, size_t ctx_size, gfp_t flags) +{ + struct perf_ctx_data *ctx_data = task->perf_ctx_data; + + lockdep_assert_held_once(&task->perf_ctx_data_lock); + + if (ctx_data) { + ctx_data->refcount++; + return 0; + } + + return __alloc_task_ctx_data_rcu(task, ctx_size, flags); +} + +/** + * Init perf_ctx_data for a task + * @task: Target Task + * @ctx_size: Size of PMU specific data + * @flags: Allocation flags + * + * If the perf_ctx_data has been allocated, update the refcount. + * Otherwise, allocate perf_ctx_data and update the RCU pointer. + * Lock is required to sync the writers of perf_ctx_data RCU pointer + * and refcount. + */ +static int +init_task_ctx_data_rcu(struct task_struct *task, size_t ctx_size, gfp_t flags) +{ + unsigned long lock_flags; + int ret; + + raw_spin_lock_irqsave(&task->perf_ctx_data_lock, lock_flags); + ret = __init_task_ctx_data_rcu(task, ctx_size, flags); + raw_spin_unlock_irqrestore(&task->perf_ctx_data_lock, lock_flags); + + return ret; +} + +static void +free_perf_ctx_data(struct rcu_head *rcu_head) +{ + struct perf_ctx_data *ctx_data; + + ctx_data = container_of(rcu_head, struct perf_ctx_data, rcu_head); + kfree(ctx_data->data); + kfree(ctx_data); +} + +/** + * Free perf_ctx_data RCU pointer for a task + * @task: Target Task + * @force: Unconditionally free perf_ctx_data + * + * If force is set, free perf_ctx_data unconditionally. + * Otherwise, free perf_ctx_data when there are no users. + * Lock is required to sync the writers of perf_ctx_data RCU pointer + * and refcount. + */ +static void +fini_task_ctx_data_rcu(struct task_struct *task, bool force) +{ + struct perf_ctx_data *ctx_data; + unsigned long flags; + + raw_spin_lock_irqsave(&task->perf_ctx_data_lock, flags); + + ctx_data = task->perf_ctx_data; + if (!ctx_data) + goto unlock; + + if (!force && --ctx_data->refcount) + goto unlock; + + RCU_INIT_POINTER(task->perf_ctx_data, NULL); + call_rcu(&ctx_data->rcu_head, free_perf_ctx_data); + +unlock: + raw_spin_unlock_irqrestore(&task->perf_ctx_data_lock, flags); +} + static void unaccount_event(struct perf_event *event) { bool dec = false; -- 2.7.4