From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: "Li, Fei1" 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 --- 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 * */ +#define pr_fmt(fmt) "ACRN HVLog: " fmt + #include #include +#include +#include +#include +#include +#include + +#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