ANDROID: KVM: arm64: add hyp_trace_printk to redirect hyp tracing to printk
This option is the hypervisor equivalent of tp_printk. This intends for
debug only and can't be disabled once set in the command line.
Bug: 357781595
Change-Id: I101985288d79dc8988f9599dcb7c50e4902f9a79
Signed-off-by: Vincent Donnefort <vdonnefort@google.com>
diff --git a/arch/arm64/include/asm/kvm_hyptrace.h b/arch/arm64/include/asm/kvm_hyptrace.h
index 7b66bd0..09b7b08 100644
--- a/arch/arm64/include/asm/kvm_hyptrace.h
+++ b/arch/arm64/include/asm/kvm_hyptrace.h
@@ -8,7 +8,7 @@
#include <linux/workqueue.h>
struct ht_iterator {
- struct trace_buffer *trace_buffer;
+ struct hyp_trace_buffer *hyp_buffer;
int cpu;
struct hyp_entry_hdr *ent;
unsigned long lost_events;
diff --git a/arch/arm64/kvm/hyp_trace.c b/arch/arm64/kvm/hyp_trace.c
index 5e39e92..cca2e4c 100644
--- a/arch/arm64/kvm/hyp_trace.c
+++ b/arch/arm64/kvm/hyp_trace.c
@@ -46,6 +46,8 @@ static struct hyp_trace_buffer {
int nr_readers;
struct mutex lock;
struct hyp_trace_clock clock;
+ struct ht_iterator *printk_iter;
+ bool printk_on;
} hyp_trace_buffer = {
.lock = __MUTEX_INITIALIZER(hyp_trace_buffer.lock),
};
@@ -92,6 +94,15 @@ bpage_backing_free(struct hyp_buffer_pages_backing *bpage_backing)
free_pages_exact((void *)bpage_backing->start, bpage_backing->size);
}
+static int set_ht_printk_on(char *str)
+{
+ if ((strcmp(str, "=0") != 0 && strcmp(str, "=off") != 0))
+ hyp_trace_buffer.printk_on = true;
+
+ return 1;
+}
+__setup("hyp_trace_printk", set_ht_printk_on);
+
static void __hyp_clock_work(struct work_struct *work)
{
struct delayed_work *dwork = to_delayed_work(work);
@@ -582,16 +593,17 @@ static int ht_print_trace_fmt(struct ht_iterator *iter)
static struct ring_buffer_event *__ht_next_pipe_event(struct ht_iterator *iter)
{
+ struct trace_buffer *trace_buffer = iter->hyp_buffer->trace_buffer;
struct ring_buffer_event *evt = NULL;
int cpu = iter->cpu;
if (cpu != RING_BUFFER_ALL_CPUS) {
- if (ring_buffer_empty_cpu(iter->trace_buffer, cpu))
+ if (ring_buffer_empty_cpu(trace_buffer, cpu))
return NULL;
iter->ent_cpu = cpu;
- return ring_buffer_peek(iter->trace_buffer, cpu, &iter->ts,
+ return ring_buffer_peek(trace_buffer, cpu, &iter->ts,
&iter->lost_events);
}
@@ -601,10 +613,10 @@ static struct ring_buffer_event *__ht_next_pipe_event(struct ht_iterator *iter)
unsigned long lost_events;
u64 ts;
- if (ring_buffer_empty_cpu(iter->trace_buffer, cpu))
+ if (ring_buffer_empty_cpu(trace_buffer, cpu))
continue;
- _evt = ring_buffer_peek(iter->trace_buffer, cpu, &ts,
+ _evt = ring_buffer_peek(trace_buffer, cpu, &ts,
&lost_events);
if (!_evt)
continue;
@@ -640,6 +652,7 @@ hyp_trace_pipe_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->hyp_buffer->trace_buffer;
int ret;
copy_to_user:
@@ -649,7 +662,7 @@ hyp_trace_pipe_read(struct file *file, char __user *ubuf,
trace_seq_init(&iter->seq);
- ret = ring_buffer_wait(iter->trace_buffer, iter->cpu, 0, NULL, NULL);
+ ret = ring_buffer_wait(trace_buffer, iter->cpu, 0, NULL, NULL);
if (ret < 0)
return ret;
@@ -661,13 +674,14 @@ hyp_trace_pipe_read(struct file *file, char __user *ubuf,
break;
}
- ring_buffer_consume(iter->trace_buffer, iter->ent_cpu, NULL,
- NULL);
+ ring_buffer_consume(trace_buffer, iter->ent_cpu, NULL, NULL);
}
goto copy_to_user;
}
+static void hyp_trace_buffer_printk(struct hyp_trace_buffer *hyp_buffer);
+
static void __poll_writer(struct work_struct *work)
{
struct delayed_work *dwork = to_delayed_work(work);
@@ -675,20 +689,21 @@ static void __poll_writer(struct work_struct *work)
iter = container_of(dwork, struct ht_iterator, poll_work);
- ring_buffer_poll_writer(iter->trace_buffer, iter->cpu);
+ ring_buffer_poll_writer(iter->hyp_buffer->trace_buffer, iter->cpu);
+
+ hyp_trace_buffer_printk(iter->hyp_buffer);
schedule_delayed_work((struct delayed_work *)work,
msecs_to_jiffies(RB_POLL_MS));
}
-static int hyp_trace_pipe_open(struct inode *inode, struct file *file)
+static struct ht_iterator *
+ht_iterator_create(struct hyp_trace_buffer *hyp_buffer, int cpu)
{
- struct hyp_trace_buffer *hyp_buffer = &hyp_trace_buffer;
- int cpu = (s64)inode->i_private;
struct ht_iterator *iter = NULL;
int ret;
- mutex_lock(&hyp_buffer->lock);
+ WARN_ON(!mutex_is_locked(&hyp_buffer->lock));
if (hyp_buffer->nr_readers == INT_MAX) {
ret = -EBUSY;
@@ -704,10 +719,9 @@ static int hyp_trace_pipe_open(struct inode *inode, struct file *file)
ret = -ENOMEM;
goto unlock;
}
- iter->trace_buffer = hyp_buffer->trace_buffer;
+ iter->hyp_buffer = hyp_buffer;
iter->cpu = cpu;
trace_seq_init(&iter->seq);
- file->private_data = iter;
ret = ring_buffer_poll_writer(hyp_buffer->trace_buffer, cpu);
if (ret)
@@ -722,11 +736,24 @@ static int hyp_trace_pipe_open(struct inode *inode, struct file *file)
if (ret) {
hyp_trace_buffer_teardown(hyp_buffer);
kfree(iter);
+ iter = NULL;
}
+ return iter;
+}
+
+static int hyp_trace_pipe_open(struct inode *inode, struct file *file)
+{
+ struct hyp_trace_buffer *hyp_buffer = &hyp_trace_buffer;
+ int cpu = (s64)inode->i_private;
+
+ mutex_lock(&hyp_buffer->lock);
+
+ file->private_data = ht_iterator_create(hyp_buffer, cpu);
+
mutex_unlock(&hyp_buffer->lock);
- return ret;
+ return file->private_data ? 0 : -EINVAL;
}
static int hyp_trace_pipe_release(struct inode *inode, struct file *file)
@@ -768,15 +795,16 @@ hyp_trace_raw_read(struct file *file, char __user *ubuf,
goto read;
again:
- ret = ring_buffer_read_page(iter->trace_buffer,
+ ret = ring_buffer_read_page(iter->hyp_buffer->trace_buffer,
(struct buffer_data_read_page *)iter->spare,
cnt, iter->cpu, 0);
if (ret < 0) {
- if (!ring_buffer_empty_cpu(iter->trace_buffer, iter->cpu))
+ if (!ring_buffer_empty_cpu(iter->hyp_buffer->trace_buffer,
+ iter->cpu))
return 0;
- ret = ring_buffer_wait(iter->trace_buffer, iter->cpu, 0, NULL,
- NULL);
+ ret = ring_buffer_wait(iter->hyp_buffer->trace_buffer,
+ iter->cpu, 0, NULL, NULL);
if (ret < 0)
return ret;
@@ -810,7 +838,8 @@ static int hyp_trace_raw_open(struct inode *inode, struct file *file)
return ret;
iter = file->private_data;
- iter->spare = ring_buffer_alloc_read_page(iter->trace_buffer, iter->cpu);
+ iter->spare = ring_buffer_alloc_read_page(iter->hyp_buffer->trace_buffer,
+ iter->cpu);
if (IS_ERR(iter->spare)) {
ret = PTR_ERR(iter->spare);
iter->spare = NULL;
@@ -824,7 +853,8 @@ 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);
+ ring_buffer_free_read_page(iter->hyp_buffer->trace_buffer, iter->cpu,
+ iter->spare);
return hyp_trace_pipe_release(inode, file);
}
@@ -917,6 +947,49 @@ static void hyp_trace_init_testing_tracefs(struct dentry *root)
static void hyp_trace_init_testing_tracefs(struct dentry *root) { }
#endif
+static int hyp_trace_buffer_printk_init(struct hyp_trace_buffer *hyp_buffer)
+{
+ int ret = 0;
+
+ mutex_lock(&hyp_buffer->lock);
+
+ if (hyp_buffer->printk_iter)
+ goto unlock;
+
+ hyp_buffer->printk_iter = ht_iterator_create(hyp_buffer,
+ RING_BUFFER_ALL_CPUS);
+ if (!hyp_buffer->printk_iter)
+ ret = -EINVAL;
+unlock:
+ mutex_unlock(&hyp_buffer->lock);
+
+ return ret;
+}
+
+static void hyp_trace_buffer_printk(struct hyp_trace_buffer *hyp_buffer)
+{
+ struct ht_iterator *ht_iter = hyp_buffer->printk_iter;
+
+ if (!hyp_trace_buffer.printk_on)
+ return;
+
+ trace_seq_init(&ht_iter->seq);
+ while (ht_next_pipe_event(ht_iter)) {
+ ht_print_trace_fmt(ht_iter);
+
+ /* Nothing has been written in the seq_buf */
+ if (!ht_iter->seq.seq.len)
+ return;
+
+ ht_iter->seq.buffer[ht_iter->seq.seq.len] = '\0';
+ printk("%s", ht_iter->seq.buffer);
+
+ ht_iter->seq.seq.len = 0;
+ ring_buffer_consume(hyp_buffer->trace_buffer, ht_iter->ent_cpu,
+ NULL, NULL);
+ }
+}
+
int hyp_trace_init_tracefs(void)
{
struct dentry *root, *per_cpu_root;
@@ -978,6 +1051,10 @@ int hyp_trace_init_tracefs(void)
hyp_trace_init_event_tracefs(root);
hyp_trace_init_testing_tracefs(root);
+ if (hyp_trace_buffer.printk_on &&
+ hyp_trace_buffer_printk_init(&hyp_trace_buffer))
+ pr_warn("Failed to init ht_printk");
+
if (hyp_trace_init_event_early()) {
err = hyp_trace_start();
if (err)