502 lines
12 KiB
Diff
502 lines
12 KiB
Diff
From 1833db605156dba32c20a3757cb773128ff54620 Mon Sep 17 00:00:00 2001
|
|
From: "Li, Fei1" <fei1.li@intel.com>
|
|
Date: Fri, 31 Aug 2018 10:58:59 +0800
|
|
Subject: [PATCH 517/743] HVLog: add HVLog module
|
|
|
|
Change-Id: I328bee769ea93dacf1642e4ffc142adb66356d2a
|
|
Tracked-On:220304
|
|
Signed-off-by: Li, Fei1 <fei1.li@intel.com>
|
|
---
|
|
drivers/acrn/acrn_hvlog.c | 349 ++++++++++++++++++++++++++++++++++++++
|
|
drivers/acrn/acrn_trace.c | 6 +-
|
|
drivers/acrn/sbuf.c | 34 +++-
|
|
drivers/acrn/sbuf.h | 11 +-
|
|
4 files changed, 393 insertions(+), 7 deletions(-)
|
|
|
|
diff --git a/drivers/acrn/acrn_hvlog.c b/drivers/acrn/acrn_hvlog.c
|
|
index 9c30fba58faf..6d27b79cb1a1 100644
|
|
--- a/drivers/acrn/acrn_hvlog.c
|
|
+++ b/drivers/acrn/acrn_hvlog.c
|
|
@@ -52,8 +52,39 @@
|
|
* Li Fei <fei1.li@intel.com>
|
|
*
|
|
*/
|
|
+#define pr_fmt(fmt) "ACRN HVLog: " fmt
|
|
+
|
|
#include <linux/memblock.h>
|
|
#include <linux/kernel.h>
|
|
+#include <linux/fs.h>
|
|
+#include <linux/uaccess.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/major.h>
|
|
+#include <linux/miscdevice.h>
|
|
+
|
|
+#include "sbuf.h"
|
|
+
|
|
+#define LOG_ENTRY_SIZE 80
|
|
+#define PCPU_NRS 4
|
|
+
|
|
+#define foreach_cpu(cpu, cpu_num) \
|
|
+ for ((cpu) = 0; (cpu) < (cpu_num); (cpu)++)
|
|
+
|
|
+#define foreach_hvlog_type(idx, hvlog_type) \
|
|
+ for ((idx) = 0; (idx) < (hvlog_type); (idx)++)
|
|
+
|
|
+enum sbuf_hvlog_index {
|
|
+ ACRN_CURRNET_HVLOG = 0,
|
|
+ ACRN_LAST_HVLOG,
|
|
+ ACRN_HVLOG_TYPE
|
|
+};
|
|
+
|
|
+struct acrn_hvlog {
|
|
+ struct miscdevice miscdev;
|
|
+ shared_buf_t *sbuf;
|
|
+ atomic_t open_cnt;
|
|
+ int pcpu_num;
|
|
+};
|
|
|
|
static unsigned long long hvlog_buf_size;
|
|
static unsigned long long hvlog_buf_base;
|
|
@@ -78,6 +109,324 @@ static int __init early_hvlog(char *p)
|
|
return ret;
|
|
}
|
|
}
|
|
+
|
|
return 0;
|
|
}
|
|
early_param("hvlog", early_hvlog);
|
|
+
|
|
+
|
|
+static inline shared_buf_t *hvlog_mark_unread(shared_buf_t *sbuf)
|
|
+{
|
|
+ /* sbuf must point to valid data.
|
|
+ * clear the lowest bit in the magic to indicate that
|
|
+ * the sbuf point to the last boot valid data, we should
|
|
+ * read it later.
|
|
+ */
|
|
+ if (sbuf != NULL)
|
|
+ sbuf->magic &= ~1;
|
|
+
|
|
+ return sbuf;
|
|
+}
|
|
+
|
|
+static int acrn_hvlog_open(struct inode *inode, struct file *filp)
|
|
+{
|
|
+ struct acrn_hvlog *acrn_hvlog;
|
|
+
|
|
+ acrn_hvlog = container_of(filp->private_data,
|
|
+ struct acrn_hvlog, miscdev);
|
|
+ pr_debug("%s, %s\n", __func__, acrn_hvlog->miscdev.name);
|
|
+
|
|
+ if (acrn_hvlog->pcpu_num >= PCPU_NRS) {
|
|
+ pr_err("%s, invalid pcpu_num: %d\n",
|
|
+ __func__, acrn_hvlog->pcpu_num);
|
|
+ return -EIO;
|
|
+ }
|
|
+
|
|
+ /* More than one reader at the same time could get data messed up */
|
|
+ if (atomic_cmpxchg(&acrn_hvlog->open_cnt, 0, 1) != 0)
|
|
+ return -EBUSY;
|
|
+
|
|
+ filp->private_data = acrn_hvlog;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int acrn_hvlog_release(struct inode *inode, struct file *filp)
|
|
+{
|
|
+ struct acrn_hvlog *acrn_hvlog;
|
|
+
|
|
+ acrn_hvlog = filp->private_data;
|
|
+
|
|
+ pr_debug("%s, %s\n", __func__, acrn_hvlog->miscdev.name);
|
|
+
|
|
+ if (acrn_hvlog->pcpu_num >= PCPU_NRS) {
|
|
+ pr_err("%s, invalid pcpu_num: %d\n",
|
|
+ __func__, acrn_hvlog->pcpu_num);
|
|
+ return -EIO;
|
|
+ }
|
|
+
|
|
+ atomic_dec(&acrn_hvlog->open_cnt);
|
|
+ filp->private_data = NULL;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static ssize_t acrn_hvlog_read(struct file *filp, char __user *buf,
|
|
+ size_t count, loff_t *offset)
|
|
+{
|
|
+ char data[LOG_ENTRY_SIZE];
|
|
+ struct acrn_hvlog *acrn_hvlog;
|
|
+ int ret;
|
|
+
|
|
+ acrn_hvlog = (struct acrn_hvlog *)filp->private_data;
|
|
+
|
|
+ pr_debug("%s, %s\n", __func__, acrn_hvlog->miscdev.name);
|
|
+
|
|
+ if (acrn_hvlog->pcpu_num >= PCPU_NRS) {
|
|
+ pr_err("%s, invalid pcpu_num: %d\n",
|
|
+ __func__, acrn_hvlog->pcpu_num);
|
|
+ return -EIO;
|
|
+ }
|
|
+
|
|
+ if (acrn_hvlog->sbuf != NULL) {
|
|
+ ret = sbuf_get(acrn_hvlog->sbuf, (uint8_t *)&data);
|
|
+ if (ret > 0) {
|
|
+ if (copy_to_user(buf, &data, ret))
|
|
+ return -EFAULT;
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct file_operations acrn_hvlog_fops = {
|
|
+ .owner = THIS_MODULE,
|
|
+ .open = acrn_hvlog_open,
|
|
+ .release = acrn_hvlog_release,
|
|
+ .read = acrn_hvlog_read,
|
|
+};
|
|
+
|
|
+static struct acrn_hvlog acrn_hvlog_devs[ACRN_HVLOG_TYPE][PCPU_NRS] = {
|
|
+ [ACRN_CURRNET_HVLOG] = {
|
|
+ {
|
|
+ .miscdev = {
|
|
+ .name = "acrn_hvlog_cur_0",
|
|
+ .minor = MISC_DYNAMIC_MINOR,
|
|
+ .fops = &acrn_hvlog_fops,
|
|
+ },
|
|
+ .pcpu_num = 0,
|
|
+ },
|
|
+ {
|
|
+ .miscdev = {
|
|
+ .name = "acrn_hvlog_cur_1",
|
|
+ .minor = MISC_DYNAMIC_MINOR,
|
|
+ .fops = &acrn_hvlog_fops,
|
|
+ },
|
|
+ .pcpu_num = 1,
|
|
+ },
|
|
+ {
|
|
+ .miscdev = {
|
|
+ .name = "acrn_hvlog_cur_2",
|
|
+ .minor = MISC_DYNAMIC_MINOR,
|
|
+ .fops = &acrn_hvlog_fops,
|
|
+ },
|
|
+ .pcpu_num = 2,
|
|
+ },
|
|
+ {
|
|
+ .miscdev = {
|
|
+ .name = "acrn_hvlog_cur_3",
|
|
+ .minor = MISC_DYNAMIC_MINOR,
|
|
+ .fops = &acrn_hvlog_fops,
|
|
+ },
|
|
+ .pcpu_num = 3,
|
|
+ },
|
|
+ },
|
|
+ [ACRN_LAST_HVLOG] = {
|
|
+ {
|
|
+ .miscdev = {
|
|
+ .name = "acrn_hvlog_last_0",
|
|
+ .minor = MISC_DYNAMIC_MINOR,
|
|
+ .fops = &acrn_hvlog_fops,
|
|
+ },
|
|
+ .pcpu_num = 0,
|
|
+ },
|
|
+ {
|
|
+ .miscdev = {
|
|
+ .name = "acrn_hvlog_last_1",
|
|
+ .minor = MISC_DYNAMIC_MINOR,
|
|
+ .fops = &acrn_hvlog_fops,
|
|
+ },
|
|
+ .pcpu_num = 1,
|
|
+ },
|
|
+ {
|
|
+ .miscdev = {
|
|
+ .name = "acrn_hvlog_last_2",
|
|
+ .minor = MISC_DYNAMIC_MINOR,
|
|
+ .fops = &acrn_hvlog_fops,
|
|
+ },
|
|
+ .pcpu_num = 2,
|
|
+ },
|
|
+ {
|
|
+ .miscdev = {
|
|
+ .name = "acrn_hvlog_last_3",
|
|
+ .minor = MISC_DYNAMIC_MINOR,
|
|
+ .fops = &acrn_hvlog_fops,
|
|
+ },
|
|
+ .pcpu_num = 3,
|
|
+ },
|
|
+ }
|
|
+};
|
|
+
|
|
+static int __init acrn_hvlog_init(void)
|
|
+{
|
|
+ int ret = 0;
|
|
+ int i, j, idx;
|
|
+ uint32_t pcpu_id;
|
|
+ uint64_t logbuf_base0;
|
|
+ uint64_t logbuf_base1;
|
|
+ uint64_t logbuf_size;
|
|
+ uint32_t ele_size;
|
|
+ uint32_t ele_num;
|
|
+ uint32_t size;
|
|
+ bool sbuf_constructed = false;
|
|
+
|
|
+ shared_buf_t *sbuf0[PCPU_NRS];
|
|
+ shared_buf_t *sbuf1[PCPU_NRS];
|
|
+
|
|
+ pr_info("%s\n", __func__);
|
|
+ if (!hvlog_buf_base || !hvlog_buf_size) {
|
|
+ pr_warn("no fixed memory reserve for hvlog.\n");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ logbuf_base0 = hvlog_buf_base;
|
|
+ logbuf_size = (hvlog_buf_size >> 1);
|
|
+ logbuf_base1 = hvlog_buf_base + logbuf_size;
|
|
+
|
|
+ size = (logbuf_size / PCPU_NRS);
|
|
+ ele_size = LOG_ENTRY_SIZE;
|
|
+ ele_num = (size - SBUF_HEAD_SIZE) / ele_size;
|
|
+
|
|
+ foreach_cpu(pcpu_id, PCPU_NRS) {
|
|
+ sbuf0[pcpu_id] = sbuf_check_valid(ele_num, ele_size,
|
|
+ logbuf_base0 + size * pcpu_id);
|
|
+ sbuf1[pcpu_id] = sbuf_check_valid(ele_num, ele_size,
|
|
+ logbuf_base1 + size * pcpu_id);
|
|
+ }
|
|
+
|
|
+ foreach_cpu(pcpu_id, PCPU_NRS) {
|
|
+ if (sbuf0[pcpu_id] == NULL)
|
|
+ continue;
|
|
+
|
|
+ foreach_cpu(pcpu_id, PCPU_NRS) {
|
|
+ acrn_hvlog_devs[ACRN_LAST_HVLOG][pcpu_id].sbuf =
|
|
+ hvlog_mark_unread(sbuf0[pcpu_id]);
|
|
+ acrn_hvlog_devs[ACRN_CURRNET_HVLOG][pcpu_id].sbuf =
|
|
+ sbuf_construct(ele_num, ele_size,
|
|
+ logbuf_base1 + size * pcpu_id);
|
|
+ }
|
|
+ sbuf_constructed = true;
|
|
+ }
|
|
+
|
|
+ if (sbuf_constructed == false) {
|
|
+ foreach_cpu(pcpu_id, PCPU_NRS) {
|
|
+ if (sbuf1[pcpu_id] == NULL)
|
|
+ continue;
|
|
+
|
|
+ foreach_cpu(pcpu_id, PCPU_NRS) {
|
|
+ acrn_hvlog_devs[ACRN_LAST_HVLOG][pcpu_id].sbuf =
|
|
+ hvlog_mark_unread(sbuf1[pcpu_id]);
|
|
+ }
|
|
+ }
|
|
+ foreach_cpu(pcpu_id, PCPU_NRS) {
|
|
+ acrn_hvlog_devs[ACRN_CURRNET_HVLOG][pcpu_id].sbuf =
|
|
+ sbuf_construct(ele_num, ele_size,
|
|
+ logbuf_base0 + size * pcpu_id);
|
|
+ }
|
|
+ sbuf_constructed = true;
|
|
+ }
|
|
+
|
|
+ idx = ACRN_CURRNET_HVLOG;
|
|
+ {
|
|
+ foreach_cpu(pcpu_id, PCPU_NRS) {
|
|
+ ret = sbuf_share_setup(pcpu_id, ACRN_HVLOG,
|
|
+ acrn_hvlog_devs[idx][pcpu_id].sbuf);
|
|
+ if (ret < 0) {
|
|
+ pr_err("Failed to setup %s, errno %d\n",
|
|
+ acrn_hvlog_devs[idx][pcpu_id].miscdev.name, ret);
|
|
+ goto setup_err;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ foreach_hvlog_type(idx, ACRN_HVLOG_TYPE) {
|
|
+ foreach_cpu(pcpu_id, PCPU_NRS) {
|
|
+ atomic_set(&acrn_hvlog_devs[idx][pcpu_id].open_cnt, 0);
|
|
+
|
|
+ ret = misc_register(
|
|
+ &acrn_hvlog_devs[idx][pcpu_id].miscdev);
|
|
+ if (ret < 0) {
|
|
+ pr_err("Failed to register %s, errno %d\n",
|
|
+ acrn_hvlog_devs[idx][pcpu_id].miscdev.name, ret);
|
|
+ goto reg_err;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+
|
|
+reg_err:
|
|
+ foreach_hvlog_type(i, idx) {
|
|
+ foreach_cpu(j, PCPU_NRS) {
|
|
+ misc_deregister(&acrn_hvlog_devs[i][j].miscdev);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ foreach_cpu(j, pcpu_id) {
|
|
+ misc_deregister(&acrn_hvlog_devs[idx][j].miscdev);
|
|
+ }
|
|
+
|
|
+ pcpu_id = PCPU_NRS;
|
|
+setup_err:
|
|
+ idx = ACRN_CURRNET_HVLOG;
|
|
+ {
|
|
+ foreach_cpu(j, pcpu_id) {
|
|
+ sbuf_share_setup(j, ACRN_HVLOG, 0);
|
|
+ sbuf_deconstruct(acrn_hvlog_devs[idx][j].sbuf);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void __exit acrn_hvlog_exit(void)
|
|
+{
|
|
+ int idx;
|
|
+ uint32_t pcpu_id;
|
|
+
|
|
+ pr_info("%s\n", __func__);
|
|
+
|
|
+ foreach_hvlog_type(idx, ACRN_HVLOG_TYPE) {
|
|
+ foreach_cpu(pcpu_id, PCPU_NRS) {
|
|
+ misc_deregister(&acrn_hvlog_devs[idx][pcpu_id].miscdev);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ idx = ACRN_CURRNET_HVLOG;
|
|
+ {
|
|
+ foreach_cpu(pcpu_id, PCPU_NRS) {
|
|
+ sbuf_share_setup(pcpu_id, ACRN_HVLOG, 0);
|
|
+ sbuf_deconstruct(acrn_hvlog_devs[idx][pcpu_id].sbuf);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+module_init(acrn_hvlog_init);
|
|
+module_exit(acrn_hvlog_exit);
|
|
+
|
|
+MODULE_LICENSE("Dual BSD/GPL");
|
|
+MODULE_AUTHOR("Intel Corp., http://www.intel.com");
|
|
+MODULE_DESCRIPTION("Driver for the Intel ACRN Hypervisor Logmsg");
|
|
+MODULE_VERSION("0.1");
|
|
diff --git a/drivers/acrn/acrn_trace.c b/drivers/acrn/acrn_trace.c
|
|
index 856ab650acfd..d48b03625223 100644
|
|
--- a/drivers/acrn/acrn_trace.c
|
|
+++ b/drivers/acrn/acrn_trace.c
|
|
@@ -239,7 +239,7 @@ static int __init acrn_trace_init(void)
|
|
}
|
|
|
|
foreach_cpu(cpu, pcpu_num) {
|
|
- ret = sbuf_share_setup(cpu, 0, sbuf_per_cpu[cpu]);
|
|
+ ret = sbuf_share_setup(cpu, ACRN_TRACE, sbuf_per_cpu[cpu]);
|
|
if (ret < 0) {
|
|
pr_err("Failed to setup SBuf, cpuid %d\n", cpu);
|
|
goto out_sbuf;
|
|
@@ -264,7 +264,7 @@ static int __init acrn_trace_init(void)
|
|
|
|
out_sbuf:
|
|
for (i = --cpu; i >= 0; i--)
|
|
- sbuf_share_setup(i, 0, NULL);
|
|
+ sbuf_share_setup(i, ACRN_TRACE, NULL);
|
|
cpu = pcpu_num;
|
|
|
|
out_free:
|
|
@@ -288,7 +288,7 @@ static void __exit acrn_trace_exit(void)
|
|
misc_deregister(acrn_trace_devs[cpu]);
|
|
|
|
/* set sbuf pointer to NULL in HV */
|
|
- sbuf_share_setup(cpu, 0, NULL);
|
|
+ sbuf_share_setup(cpu, ACRN_TRACE, NULL);
|
|
|
|
/* free sbuf, sbuf_per_cpu[cpu] should be set NULL */
|
|
sbuf_free(sbuf_per_cpu[cpu]);
|
|
diff --git a/drivers/acrn/sbuf.c b/drivers/acrn/sbuf.c
|
|
index 8849ce28a06c..a3582325d9b9 100644
|
|
--- a/drivers/acrn/sbuf.c
|
|
+++ b/drivers/acrn/sbuf.c
|
|
@@ -185,7 +185,7 @@ int sbuf_share_setup(uint32_t pcpu_id, uint32_t sbuf_id, shared_buf_t *sbuf)
|
|
}
|
|
EXPORT_SYMBOL(sbuf_share_setup);
|
|
|
|
-shared_buf_t *sbuf_construct(uint32_t ele_num, uint32_t ele_size,
|
|
+shared_buf_t *sbuf_check_valid(uint32_t ele_num, uint32_t ele_size,
|
|
uint64_t paddr)
|
|
{
|
|
shared_buf_t *sbuf;
|
|
@@ -199,11 +199,39 @@ shared_buf_t *sbuf_construct(uint32_t ele_num, uint32_t ele_size,
|
|
if ((sbuf->magic == SBUF_MAGIC) &&
|
|
(sbuf->ele_num == ele_num) &&
|
|
(sbuf->ele_size == ele_size)) {
|
|
- pr_info("construct sbuf at 0x%llx.\n", paddr);
|
|
- /* return sbuf for dump */
|
|
return sbuf;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
+EXPORT_SYMBOL(sbuf_check_valid);
|
|
+
|
|
+shared_buf_t *sbuf_construct(uint32_t ele_num, uint32_t ele_size,
|
|
+ uint64_t paddr)
|
|
+{
|
|
+ shared_buf_t *sbuf;
|
|
+
|
|
+ if (!ele_num || !ele_size || !paddr)
|
|
+ return NULL;
|
|
+
|
|
+ sbuf = (shared_buf_t *)phys_to_virt(paddr);
|
|
+ BUG_ON(!virt_addr_valid(sbuf));
|
|
+
|
|
+ memset(sbuf, 0, SBUF_HEAD_SIZE);
|
|
+ sbuf->magic = SBUF_MAGIC;
|
|
+ sbuf->ele_num = ele_num;
|
|
+ sbuf->ele_size = ele_size;
|
|
+ sbuf->size = ele_num * ele_size;
|
|
+ pr_info("construct sbuf at 0x%llx.\n", paddr);
|
|
+ return sbuf;
|
|
+}
|
|
EXPORT_SYMBOL(sbuf_construct);
|
|
+
|
|
+void sbuf_deconstruct(shared_buf_t *sbuf)
|
|
+{
|
|
+ if (sbuf == NULL)
|
|
+ return;
|
|
+
|
|
+ sbuf->magic = 0;
|
|
+}
|
|
+EXPORT_SYMBOL(sbuf_deconstruct);
|
|
diff --git a/drivers/acrn/sbuf.h b/drivers/acrn/sbuf.h
|
|
index 73608c35046c..4fae7a258bce 100644
|
|
--- a/drivers/acrn/sbuf.h
|
|
+++ b/drivers/acrn/sbuf.h
|
|
@@ -67,6 +67,11 @@
|
|
#define OVERRUN_CNT_EN (1ULL << 0) /* whether overrun counting is enabled */
|
|
#define OVERWRITE_EN (1ULL << 1) /* whether overwrite is enabled */
|
|
|
|
+enum sbuf_type {
|
|
+ ACRN_TRACE,
|
|
+ ACRN_HVLOG,
|
|
+ ACRN_SBUF_TYPE_MAX,
|
|
+};
|
|
/**
|
|
* (sbuf) head + buf (store (ele_num - 1) elements at most)
|
|
* buffer empty: tail == head
|
|
@@ -115,6 +120,10 @@ shared_buf_t *sbuf_allocate(uint32_t ele_num, uint32_t ele_size);
|
|
void sbuf_free(shared_buf_t *sbuf);
|
|
int sbuf_get(shared_buf_t *sbuf, uint8_t *data);
|
|
int sbuf_share_setup(uint32_t pcpu_id, uint32_t sbuf_id, shared_buf_t *sbuf);
|
|
-shared_buf_t *sbuf_construct(uint32_t ele_num, uint32_t ele_size, uint64_t gpa);
|
|
+shared_buf_t *sbuf_check_valid(uint32_t ele_num, uint32_t ele_size,
|
|
+ uint64_t gpa);
|
|
+shared_buf_t *sbuf_construct(uint32_t ele_num, uint32_t ele_size,
|
|
+ uint64_t gpa);
|
|
+void sbuf_deconstruct(shared_buf_t *sbuf);
|
|
|
|
#endif /* SHARED_BUF_H */
|
|
--
|
|
2.19.2
|
|
|