From 2cec790c70ea930ce4a5fe6d054b8cb984824548 Mon Sep 17 00:00:00 2001 From: Vincent Donnefort Date: Tue, 14 Feb 2023 19:21:22 +0000 Subject: [PATCH] ANDROID: KVM: arm64: Allow to reset hyp trace buffers Writing anything to the "trace" file will delete the content of the buffer. When using the common "trace", the ring buffer will also be unloaded from the hypervisor and all the memory will be freed. At the same time, tracing_on will not reset the buffers anymore and trace pipe interfaces will be able to setup the ring buffers, bringing the hyp tracing interface a bit closer from the host behavior. Bug: 249050813 Change-Id: I9d4ba7b18504440f3d03dbedf1186d384a53a990 Signed-off-by: Vincent Donnefort --- arch/arm64/kvm/hyp_trace.c | 167 +++++++++++++++++++++++++++---------- 1 file changed, 123 insertions(+), 44 deletions(-) diff --git a/arch/arm64/kvm/hyp_trace.c b/arch/arm64/kvm/hyp_trace.c index f669bac8e224..bc385c3d784f 100644 --- a/arch/arm64/kvm/hyp_trace.c +++ b/arch/arm64/kvm/hyp_trace.c @@ -20,8 +20,11 @@ #define RB_POLL_MS 1000 #define TRACEFS_DIR "hyp" +#define TRACEFS_MODE_WRITE 0640 +#define TRACEFS_MODE_READ 0440 static bool hyp_trace_on; +static bool hyp_free_tracing_deferred; static int hyp_trace_readers; static struct trace_buffer *hyp_trace_buffer; static size_t hyp_trace_buffer_size = 7 << 10; @@ -198,25 +201,11 @@ end: hyp_trace_buffer = NULL; } -static void hyp_free_tracing(void) -{ - if (!hyp_trace_buffer) - return; - - trace_buffer_teardown(NULL); - bpage_backing_teardown(); -} - -static int hyp_start_tracing(void) +static int hyp_load_tracing(void) { struct hyp_trace_pack *pack; size_t pack_size; - int ret = 0; - - if (hyp_trace_on || hyp_trace_readers) - return -EBUSY; - - hyp_free_tracing(); + int ret; ret = trace_buffer_setup(&pack, &pack_size); if (ret) @@ -229,15 +218,9 @@ static int hyp_start_tracing(void) goto end_buffer_teardown; ret = kvm_call_hyp_nvhe(__pkvm_load_tracing, (unsigned long)pack, pack_size); - if (ret) - goto end_backing_teardown; - - ret = kvm_call_hyp_nvhe(__pkvm_enable_tracing, true); - if (!ret) { - hyp_trace_on = true; + if (!ret) goto end_free_pack; - } -end_backing_teardown: + bpage_backing_teardown(); end_buffer_teardown: trace_buffer_teardown(&pack->trace_buffer_pack); @@ -247,6 +230,37 @@ end_free_pack: return ret; } +static void hyp_free_tracing(void) +{ + WARN_ON(hyp_trace_readers || hyp_trace_on); + + if (WARN_ON(kvm_call_hyp_nvhe(__pkvm_teardown_tracing))) + return; + + trace_buffer_teardown(NULL); + bpage_backing_teardown(); +} + +static int hyp_start_tracing(void) +{ + int ret = 0; + + if (hyp_trace_on) + return -EBUSY; + + if (!hyp_trace_buffer) { + ret = hyp_load_tracing(); + if (ret) + return ret; + } + + ret = kvm_call_hyp_nvhe(__pkvm_enable_tracing, true); + if (!ret) + hyp_trace_on = true; + + return ret; +} + static void hyp_stop_tracing(void) { int ret; @@ -254,7 +268,7 @@ static void hyp_stop_tracing(void) if (!hyp_trace_buffer || !hyp_trace_on) return; - ret = kvm_call_hyp_nvhe(__pkvm_teardown_tracing); + ret = kvm_call_hyp_nvhe(__pkvm_enable_tracing, false); if (ret) { WARN_ON(1); return; @@ -564,6 +578,48 @@ static const struct seq_operations hyp_trace_ops = { .show = ht_show, }; +static int hyp_trace_reset(int cpu) +{ + if (!hyp_trace_buffer) + return 0; + + if (hyp_trace_on) + return -EBUSY; + + if (cpu == RING_BUFFER_ALL_CPUS) { + if (hyp_trace_readers) + hyp_free_tracing_deferred = true; + else + hyp_free_tracing(); + + return 0; + } + + ring_buffer_reset_cpu(hyp_trace_buffer, cpu); + + return 0; +} + +static void hyp_inc_readers(void) +{ + hyp_trace_readers++; +} + +static void hyp_dec_readers(void) +{ + hyp_trace_readers--; + + WARN_ON(hyp_trace_readers < 0); + + if (hyp_trace_readers) + return; + + if (hyp_free_tracing_deferred) { + hyp_free_tracing(); + hyp_free_tracing_deferred = false; + } +} + static int hyp_trace_open(struct inode *inode, struct file *file) { int cpu = (s64)inode->i_private; @@ -572,6 +628,11 @@ static int hyp_trace_open(struct inode *inode, struct file *file) mutex_lock(&hyp_trace_lock); + if (file->f_mode & FMODE_WRITE) { + ret = hyp_trace_reset(cpu); + goto unlock; + } + iter = __seq_open_private(file, &hyp_trace_ops, sizeof(*iter)); if (!iter) { ret = -ENOMEM; @@ -614,7 +675,7 @@ static int hyp_trace_open(struct inode *inode, struct file *file) ring_buffer_read_start(iter->buf_iter[cpu]); } unlock_and_read: - hyp_trace_readers++; + hyp_inc_readers(); unlock: if (ret && iter) { kfree(iter->buf_iter); @@ -631,6 +692,9 @@ int hyp_trace_release(struct inode *inode, struct file *file) struct seq_file *m = file->private_data; struct ht_iterator *iter = m->private; + if (file->f_mode & FMODE_WRITE) + return 0; + if (!iter->buf_iter) goto end; @@ -647,17 +711,25 @@ int hyp_trace_release(struct inode *inode, struct file *file) kfree(iter->buf_iter); end: mutex_lock(&hyp_trace_lock); - hyp_trace_readers--; + hyp_dec_readers(); mutex_unlock(&hyp_trace_lock); return seq_release_private(inode, file); } +static ssize_t hyp_trace_write(struct file *filp, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + /* No matter the input, writing resets the buffer */ + return count; +} + static const struct file_operations hyp_trace_fops = { - .open = hyp_trace_open, - .read = seq_read, - .llseek = seq_lseek, - .release = hyp_trace_release, + .open = hyp_trace_open, + .read = seq_read, + .write = hyp_trace_write, + .llseek = seq_lseek, + .release = hyp_trace_release, }; /* @@ -728,12 +800,15 @@ static int hyp_trace_pipe_open(struct inode *inode, struct file *file) { int cpu = (s64)inode->i_private; struct ht_iterator *iter; - int ret = -EINVAL; + int ret; mutex_lock(&hyp_trace_lock); - if (!hyp_trace_buffer) - goto unlock; + if (!hyp_trace_buffer) { + ret = hyp_load_tracing(); + if (ret) + goto unlock; + } ret = ring_buffer_poke(hyp_trace_buffer, cpu); if (ret) @@ -753,7 +828,7 @@ static int hyp_trace_pipe_open(struct inode *inode, struct file *file) file->private_data = iter; - hyp_trace_readers++; + hyp_inc_readers(); unlock: mutex_unlock(&hyp_trace_lock); @@ -769,7 +844,7 @@ static int hyp_trace_pipe_release(struct inode *inode, struct file *file) kfree(iter); mutex_lock(&hyp_trace_lock); - hyp_trace_readers--; + hyp_dec_readers(); mutex_unlock(&hyp_trace_lock); return 0; @@ -863,10 +938,11 @@ static const struct file_operations hyp_trace_raw_fops = { static void hyp_tracefs_create_cpu_file(const char *file_name, int cpu, + umode_t mode, const struct file_operations *fops, struct dentry *parent) { - if (!tracefs_create_file(file_name, 0440, parent, (void *)(s64)cpu, fops)) + if (!tracefs_create_file(file_name, mode, parent, (void *)(s64)cpu, fops)) pr_warn("Failed to create tracefs %pd/%s\n", parent, file_name); } @@ -888,20 +964,21 @@ int init_hyp_tracefs(void) return -ENODEV; } - d = tracefs_create_file("tracing_on", 0640, root_dir, NULL, - &hyp_tracing_on_fops); + d = tracefs_create_file("tracing_on", TRACEFS_MODE_WRITE, root_dir, + NULL, &hyp_tracing_on_fops); if (!d) { pr_err("Failed to create tracefs "TRACEFS_DIR"/tracing_on\n"); return -ENODEV; } - d = tracefs_create_file("buffer_size_kb", 0640, root_dir, NULL, - &hyp_buffer_size_fops); + d = tracefs_create_file("buffer_size_kb", TRACEFS_MODE_WRITE, root_dir, + NULL, &hyp_buffer_size_fops); if (!d) pr_err("Failed to create tracefs "TRACEFS_DIR"/buffer_size_kb\n"); hyp_tracefs_create_cpu_file("trace", RING_BUFFER_ALL_CPUS, - &hyp_trace_fops, root_dir); + TRACEFS_MODE_WRITE, &hyp_trace_fops, + root_dir); per_cpu_root_dir = tracefs_create_dir("per_cpu", root_dir); if (!per_cpu_root_dir) { @@ -920,10 +997,12 @@ int init_hyp_tracefs(void) continue; } - hyp_tracefs_create_cpu_file("trace", cpu, &hyp_trace_fops, dir); - hyp_tracefs_create_cpu_file("trace_pipe", cpu, + hyp_tracefs_create_cpu_file("trace", cpu, TRACEFS_MODE_WRITE, + &hyp_trace_fops, dir); + hyp_tracefs_create_cpu_file("trace_pipe", cpu, TRACEFS_MODE_READ, &hyp_trace_pipe_fops, dir); hyp_tracefs_create_cpu_file("trace_pipe_raw", cpu, + TRACEFS_MODE_READ, &hyp_trace_raw_fops, dir); }