clear-pkgs-linux-iot-lts2018/0499-HVLog-add-HVLog-module...

502 lines
12 KiB
Diff

From 0000000000000000000000000000000000000000 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] 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 */
--
https://clearlinux.org