perf kvm: Reference count 'struct kvm_info'
authorLeo Yan <leo.yan@linaro.org>
Mon, 20 Mar 2023 06:16:18 +0000 (14:16 +0800)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Tue, 21 Mar 2023 13:06:05 +0000 (10:06 -0300)
hists__add_entry_ops() doesn't allocate a new histogram entry if it has
an existing entry for a KVM event, in this case, find_create_kvm_event()
allocates a 'struct kvm_info' but it's not used by any histograms and
never freed.

To fix the memory leak, this patch first introduces a refcnt and a set
of functions for refcnt operations on 'struct kvm_info'.  When the data
structure is not anymore used (the refcnt hits zero) kvm_info__zput()
will free the memory used.

Committer:

Provide a nop version of kvm_info__zput() to be used when
HAVE_KVM_STAT_SUPPORT isn't defined as it is used unconditionally in
hists__findnew_entry() and hist_entry__delete().

Signed-off-by: Leo Yan <leo.yan@linaro.org>
Acked-by: Ian Rogers <irogers@google.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Clark <james.clark@arm.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: https://lore.kernel.org/r/20230320061619.29520-2-leo.yan@linaro.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/builtin-kvm.c
tools/perf/util/hist.c
tools/perf/util/kvm-stat.h

index 4c205df5106f58e3bd30da9ff0bd616d696ad5f8..1e1cb5a9d0a2dda3f7e3235d6f5d9da4c26e42c8 100644 (file)
@@ -768,7 +768,6 @@ static void kvm_he_free(void *he)
 {
        struct kvm_event *kvm_ev;
 
-       free(((struct hist_entry *)he)->kvm_info);
        kvm_ev = container_of(he, struct kvm_event, he);
        free(kvm_ev);
 }
@@ -788,7 +787,7 @@ static struct kvm_event *find_create_kvm_event(struct perf_kvm_stat *kvm,
 
        BUG_ON(key->key == INVALID_KEY);
 
-       ki = zalloc(sizeof(*ki));
+       ki = kvm_info__new();
        if (!ki) {
                pr_err("Failed to allocate kvm info\n");
                return NULL;
index 0c11f50abfecf6ba46fc1330e7bc959a59a065ae..b339ff863fe2bf855b35d1a6aa666e0d78710a27 100644 (file)
@@ -628,6 +628,8 @@ static struct hist_entry *hists__findnew_entry(struct hists *hists,
 
                        block_info__zput(entry->block_info);
 
+                       kvm_info__zput(entry->kvm_info);
+
                        /* If the map of an existing hist_entry has
                         * become out-of-date due to an exec() or
                         * similar, update it.  Otherwise we will
@@ -1324,6 +1326,9 @@ void hist_entry__delete(struct hist_entry *he)
        if (he->block_info)
                block_info__zput(he->block_info);
 
+       if (he->kvm_info)
+               kvm_info__zput(he->kvm_info);
+
        zfree(&he->res_samples);
        zfree(&he->stat_acc);
        free_srcline(he->srcline);
index bc6c8e38ef500b2917c99154f4c59b41815fa8aa..3e9ac754c3d1f0cfc0078a40f21a3bd1121df4c7 100644 (file)
@@ -10,6 +10,9 @@
 #include "symbol.h"
 #include "record.h"
 
+#include <stdlib.h>
+#include <linux/zalloc.h>
+
 #define KVM_EVENT_NAME_LEN     40
 
 struct evsel;
@@ -25,6 +28,7 @@ struct event_key {
 
 struct kvm_info {
        char name[KVM_EVENT_NAME_LEN];
+       refcount_t refcnt;
 };
 
 struct kvm_event_stats {
@@ -145,6 +149,42 @@ extern const char *vcpu_id_str;
 extern const char *kvm_exit_reason;
 extern const char *kvm_entry_trace;
 extern const char *kvm_exit_trace;
+
+static inline struct kvm_info *kvm_info__get(struct kvm_info *ki)
+{
+       if (ki)
+               refcount_inc(&ki->refcnt);
+       return ki;
+}
+
+static inline void kvm_info__put(struct kvm_info *ki)
+{
+       if (ki && refcount_dec_and_test(&ki->refcnt))
+               free(ki);
+}
+
+static inline void __kvm_info__zput(struct kvm_info **ki)
+{
+       kvm_info__put(*ki);
+       *ki = NULL;
+}
+
+#define kvm_info__zput(ki) __kvm_info__zput(&ki)
+
+static inline struct kvm_info *kvm_info__new(void)
+{
+       struct kvm_info *ki;
+
+       ki = zalloc(sizeof(*ki));
+       if (ki)
+               refcount_set(&ki->refcnt, 1);
+
+       return ki;
+}
+
+#else /* HAVE_KVM_STAT_SUPPORT */
+// We use this unconditionally in hists__findnew_entry() and hist_entry__delete()
+#define kvm_info__zput(ki) do { } while (0)
 #endif /* HAVE_KVM_STAT_SUPPORT */
 
 extern int kvm_add_default_arch_event(int *argc, const char **argv);