From a7640ce6cf682aa3ec884d620a4e99eea8693709 Mon Sep 17 00:00:00 2001 From: Vincent Donnefort Date: Mon, 17 Oct 2022 13:50:45 +0100 Subject: [PATCH] ANDROID: KVM: arm64: RAW interface to the nVHE hyp tracing This interface intends to be used by userspace tools to store raw version of events. In such case, the kernel does not decode anything. Bug: 229972309 Change-Id: Ib1fca21a34a308ad1361240ef598033ecab3b4ad Signed-off-by: Vincent Donnefort --- arch/arm64/kvm/hyp_trace.c | 81 ++++++++++++++++++++++++++++++++++++++ arch/arm64/kvm/hyp_trace.h | 2 + kernel/trace/ring_buffer.c | 29 +++++++------- 3 files changed, 98 insertions(+), 14 deletions(-) diff --git a/arch/arm64/kvm/hyp_trace.c b/arch/arm64/kvm/hyp_trace.c index 8e8584a2d4c3..0ea14666e970 100644 --- a/arch/arm64/kvm/hyp_trace.c +++ b/arch/arm64/kvm/hyp_trace.c @@ -641,6 +641,85 @@ static const struct file_operations hyp_trace_pipe_fops = { .llseek = no_llseek, }; +static ssize_t +hyp_trace_raw_read(struct file *file, char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + struct ht_iterator *iter = (struct ht_iterator *)file->private_data; + struct trace_buffer *trace_buffer = iter->trace_buffer; + size_t size; + int ret; + + if (iter->copy_leftover) + goto read; +again: + hyp_trace_read_start(iter->cpu); + ret = ring_buffer_read_page(trace_buffer, &iter->spare, + cnt, iter->cpu, 0); + hyp_trace_read_stop(iter->cpu); + if (ret < 0) { + if (!ring_buffer_empty_cpu(iter->trace_buffer, iter->cpu)) + return 0; + + ret = ring_buffer_wait(trace_buffer, iter->cpu, 0); + if (ret < 0) + return ret; + + goto again; + } + + iter->copy_leftover = 0; +read: + size = PAGE_SIZE - iter->copy_leftover; + if (size > cnt) + size = cnt; + + ret = copy_to_user(ubuf, iter->spare + PAGE_SIZE - size, size); + if (ret == size) + return -EFAULT; + + size -= ret; + *ppos += size; + iter->copy_leftover = ret; + + return size; +} + +static int hyp_trace_raw_open(struct inode *inode, struct file *file) +{ + int ret = hyp_trace_pipe_open(inode, file); + struct ht_iterator *iter; + + if (ret) + return ret; + + iter = file->private_data; + iter->spare = ring_buffer_alloc_read_page(iter->trace_buffer, iter->cpu); + if (IS_ERR(iter->spare)) { + ret = PTR_ERR(iter->spare); + iter->spare = NULL; + return ret; + } + + return 0; +} + +static int hyp_trace_raw_release(struct inode *inode, struct file *file) +{ + struct ht_iterator *iter = file->private_data; + + ring_buffer_free_read_page(iter->trace_buffer, iter->cpu, iter->spare); + + return hyp_trace_pipe_release(inode, file); +} + +static const struct file_operations hyp_trace_raw_fops = { + .open = hyp_trace_raw_open, + .read = hyp_trace_raw_read, + .release = hyp_trace_raw_release, + .llseek = no_llseek, +}; + static void hyp_tracefs_create_cpu_file(const char *file_name, unsigned long cpu, const struct file_operations *fops, @@ -700,6 +779,8 @@ int init_hyp_tracefs(void) hyp_tracefs_create_cpu_file("trace", cpu, &hyp_trace_fops, dir); hyp_tracefs_create_cpu_file("trace_pipe", cpu, &hyp_trace_pipe_fops, dir); + hyp_tracefs_create_cpu_file("trace_pipe_raw", cpu, + &hyp_trace_raw_fops, dir); } kvm_hyp_init_events_tracefs(root_dir); diff --git a/arch/arm64/kvm/hyp_trace.h b/arch/arm64/kvm/hyp_trace.h index 7d062ec9bf19..fb8172e464f2 100644 --- a/arch/arm64/kvm/hyp_trace.h +++ b/arch/arm64/kvm/hyp_trace.h @@ -12,6 +12,8 @@ struct ht_iterator { struct hyp_entry_hdr *ent; struct trace_seq seq; u64 ts; + void *spare; + size_t copy_leftover; size_t ent_size; struct delayed_work poke_work; unsigned long lost_events; diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 7d1bccc16e94..4d5ce7f6c578 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -5729,9 +5729,6 @@ void *ring_buffer_alloc_read_page(struct trace_buffer *buffer, int cpu) unsigned long flags; struct page *page; - if (unlikely(has_ext_writer(buffer))) - return ERR_PTR(-EINVAL); - if (!cpumask_test_cpu(cpu, buffer->cpumask)) return ERR_PTR(-ENODEV); @@ -5846,9 +5843,6 @@ int ring_buffer_read_page(struct trace_buffer *buffer, u64 save_timestamp; int ret = -1; - if (unlikely(has_ext_writer(buffer))) - goto out; - if (!cpumask_test_cpu(cpu, buffer->cpumask)) goto out; @@ -5955,14 +5949,21 @@ int ring_buffer_read_page(struct trace_buffer *buffer, cpu_buffer->read += rb_page_entries(reader); cpu_buffer->read_bytes += BUF_PAGE_SIZE; - /* swap the pages */ - rb_init_page(bpage); - bpage = reader->page; - reader->page = *data_page; - local_set(&reader->write, 0); - local_set(&reader->entries, 0); - reader->read = 0; - *data_page = bpage; + if (unlikely(has_ext_writer(buffer))) { + u64 commit = local_read(&reader->page->commit); + + memcpy(bpage, reader->page, + BUF_PAGE_HDR_SIZE + commit); + } else { + /* swap the pages */ + rb_init_page(bpage); + bpage = reader->page; + reader->page = *data_page; + local_set(&reader->write, 0); + local_set(&reader->entries, 0); + reader->read = 0; + *data_page = bpage; + } /* * Use the real_end for the data size,