soc: qcom: hab: add hab statistics support

This allows user to read back hab runtime information.

Change-Id: Id266dd17b9c9d38f0e93aa600510ae1c6b12cca5
Signed-off-by: Yong Ding <yongding@codeaurora.org>
This commit is contained in:
Yong Ding 2018-07-31 14:16:16 +08:00
parent 7adc4e13a4
commit 4faca7cde5
10 changed files with 387 additions and 22 deletions

View file

@ -9,7 +9,8 @@ msm_hab-objs = \
hab_mem_linux.o \
hab_pipe.o \
hab_parser.o \
khab_test.o
khab_test.o \
hab_stat.o
ifdef CONFIG_GHS_VMM
msm_hab_hyp-objs = \

View file

@ -21,7 +21,7 @@
.openlock = __SPIN_LOCK_UNLOCKED(&hab_devices[__num__].openlock)\
}
static const char hab_info_str[] = "Change: 16239527 Revision: #65";
static const char hab_info_str[] = "Change: 16764735 Revision: #76";
/*
* The following has to match habmm definitions, order does not matter if
@ -283,7 +283,7 @@ struct virtual_channel *frontend_open(struct uhab_context *ctx,
pr_err("vchan alloc failed\n");
ret = -ENOMEM;
goto err;
} else
}
/* Send Init sequence */
hab_open_request_init(&request, HAB_PAYLOAD_TYPE_INIT, pchan,
@ -667,6 +667,7 @@ int hab_vchan_open(struct uhab_context *ctx,
}
} else {
pr_err("failed to find device, mmid %d\n", mmid);
return -ENODEV;
}
}
@ -1368,6 +1369,9 @@ static int __init hab_init(void)
} else
set_dma_ops(hab_driver.dev, &hab_dma_ops);
}
hab_stat_init(&hab_driver);
return result;
err:
@ -1387,6 +1391,7 @@ static void __exit hab_exit(void)
dev_t dev;
hab_hypervisor_unregister();
hab_stat_deinit(&hab_driver);
hab_ctx_put(hab_driver.kctx);
dev = MKDEV(MAJOR(hab_driver.major), 0);
device_destroy(hab_driver.class, dev);

View file

@ -43,6 +43,8 @@
#include <linux/dma-mapping.h>
#include <linux/jiffies.h>
#include <linux/reboot.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
enum hab_payload_type {
HAB_PAYLOAD_TYPE_MSG = 0x0,
@ -338,6 +340,8 @@ struct hab_driver {
int b_loopback;
void *hyp_priv; /* hypervisor plug-in storage */
void *hab_vmm_handle;
};
struct virtual_channel {
@ -412,6 +416,7 @@ int hab_vchan_recv(struct uhab_context *ctx,
void hab_vchan_stop(struct virtual_channel *vchan);
void hab_vchans_stop(struct physical_channel *pchan);
void hab_vchan_stop_notify(struct virtual_channel *vchan);
void hab_vchans_empty_wait(int vmid);
int hab_mem_export(struct uhab_context *ctx,
struct hab_export *param, int kernel);
@ -456,7 +461,7 @@ int habmm_imp_hyp_unmap(void *imp_ctx, struct export_desc *exp, int kernel);
int habmem_imp_hyp_mmap(struct file *flip, struct vm_area_struct *vma);
int habmm_imp_hyp_map_check(void *imp_ctx, struct export_desc *exp);
void hab_msg_free(struct hab_message *message);
int hab_msg_dequeue(struct virtual_channel *vchan,
@ -563,6 +568,15 @@ int hab_open_cancel_notify(struct hab_open_request *request);
int hab_open_receive_cancel(struct physical_channel *pchan,
size_t sizebytes);
int hab_stat_init(struct hab_driver *drv);
int hab_stat_deinit(struct hab_driver *drv);
int hab_stat_show_vchan(struct hab_driver *drv, char *buf, int sz);
int hab_stat_show_ctx(struct hab_driver *drv, char *buf, int sz);
int hab_stat_show_expimp(struct hab_driver *drv, int pid, char *buf, int sz);
int hab_stat_init_sub(struct hab_driver *drv);
int hab_stat_deinit_sub(struct hab_driver *drv);
/* Global singleton HAB instance */
extern struct hab_driver hab_driver;

View file

@ -751,3 +751,22 @@ int habmem_imp_hyp_mmap(struct file *filp, struct vm_area_struct *vma)
return 0;
}
int habmm_imp_hyp_map_check(void *imp_ctx, struct export_desc *exp)
{
struct importer_context *priv = imp_ctx;
struct pages_list *pglist;
int found = 0;
read_lock(&priv->implist_lock);
list_for_each_entry(pglist, &priv->imp_list, list) {
if (pglist->export_id == exp->export_id &&
pglist->vcid == exp->vcid_remote) {
found = 1;
break;
}
}
read_unlock(&priv->implist_lock);
return found;
}

View file

@ -104,8 +104,8 @@ static struct export_desc *habmem_add_export(struct virtual_channel *vchan,
exp->vchan = vchan;
exp->vcid_local = vchan->id;
exp->vcid_remote = vchan->otherend_id;
exp->domid_local = -1; /* dom id, provided on the importer */
exp->domid_remote = vchan->pchan->dom_id;
exp->domid_local = vchan->pchan->vmid_local;
exp->domid_remote = vchan->pchan->vmid_remote;
exp->ctx = vchan->ctx;
exp->pchan = vchan->pchan;
@ -124,7 +124,8 @@ void habmem_remove_export(struct export_desc *exp)
struct uhab_context *ctx;
if (!exp || !exp->ctx || !exp->pchan) {
pr_err("failed to find valid info in exp %pK\n", exp);
pr_err("failed to find valid info in exp %pK ctx %pK pchan %pK\n",
exp, exp->ctx, exp->pchan);
return;
}

View file

@ -49,5 +49,5 @@ struct qvm_channel {
void *qnx_hyp_rx_dispatch(void *data);
void hab_pipe_reset(struct physical_channel *pchan);
void habhyp_notify(void *commdev);
#endif /* __HAB_QNX_H */

View file

@ -0,0 +1,167 @@
/* Copyright (c) 2018, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include "hab.h"
#include "hab_grantable.h"
#define MAX_LINE_SIZE 128
int hab_stat_init(struct hab_driver *driver)
{
return hab_stat_init_sub(driver);
}
int hab_stat_deinit(struct hab_driver *driver)
{
return hab_stat_deinit_sub(driver);
}
/*
* If all goes well the return value is the formated print and concatenated
* original dest string.
*/
static int hab_stat_buffer_print(char *dest,
int dest_size, const char *fmt, ...)
{
va_list args;
char line[MAX_LINE_SIZE];
int ret;
va_start(args, fmt);
ret = vsnprintf(line, sizeof(line), fmt, args);
va_end(args);
if (ret > 0)
ret = strlcat(dest, line, dest_size);
return ret;
}
int hab_stat_show_vchan(struct hab_driver *driver,
char *buf, int size)
{
int i, ret = 0;
ret = strlcpy(buf, "", size);
for (i = 0; i < driver->ndevices; i++) {
struct hab_device *dev = &driver->devp[i];
struct physical_channel *pchan;
struct virtual_channel *vc;
spin_lock_bh(&dev->pchan_lock);
list_for_each_entry(pchan, &dev->pchannels, node) {
if (!pchan->vcnt)
continue;
ret = hab_stat_buffer_print(buf, size,
"mmid %s role %d local %d remote %d vcnt %d:\n",
pchan->name, pchan->is_be, pchan->vmid_local,
pchan->vmid_remote, pchan->vcnt);
read_lock(&pchan->vchans_lock);
list_for_each_entry(vc, &pchan->vchannels, pnode) {
ret = hab_stat_buffer_print(buf, size,
"%08X ", vc->id);
}
ret = hab_stat_buffer_print(buf, size, "\n");
read_unlock(&pchan->vchans_lock);
}
spin_unlock_bh(&dev->pchan_lock);
}
return ret;
}
int hab_stat_show_ctx(struct hab_driver *driver,
char *buf, int size)
{
int ret = 0;
struct uhab_context *ctx;
ret = strlcpy(buf, "", size);
spin_lock_bh(&hab_driver.drvlock);
ret = hab_stat_buffer_print(buf, size,
"Total contexts %d\n",
driver->ctx_cnt);
list_for_each_entry(ctx, &hab_driver.uctx_list, node) {
ret = hab_stat_buffer_print(buf, size,
"ctx %d K %d close %d vc %d exp %d imp %d open %d\n",
ctx->owner, ctx->kernel, ctx->closing,
ctx->vcnt, ctx->export_total,
ctx->import_total, ctx->pending_cnt);
}
spin_unlock_bh(&hab_driver.drvlock);
return ret;
}
static int get_pft_tbl_total_size(struct compressed_pfns *pfn_table)
{
int i, total_size = 0;
for (i = 0; i < pfn_table->nregions; i++)
total_size += pfn_table->region[i].size * PAGE_SIZE;
return total_size;
}
static int print_ctx_total_expimp(struct uhab_context *ctx,
char *buf, int size)
{
struct compressed_pfns *pfn_table;
int exp_total = 0, imp_total = 0;
int exp_cnt = 0, imp_cnt = 0;
struct export_desc *exp;
read_lock(&ctx->exp_lock);
list_for_each_entry(exp, &ctx->exp_whse, node) {
pfn_table = (struct compressed_pfns *)exp->payload;
exp_total += get_pft_tbl_total_size(pfn_table);
exp_cnt++;
}
read_unlock(&ctx->exp_lock);
spin_lock_bh(&ctx->imp_lock);
list_for_each_entry(exp, &ctx->imp_whse, node) {
if (habmm_imp_hyp_map_check(ctx->import_ctx, exp)) {
pfn_table = (struct compressed_pfns *)exp->payload;
imp_total += get_pft_tbl_total_size(pfn_table);
imp_cnt++;
}
}
spin_unlock_bh(&ctx->imp_lock);
if (exp_cnt || exp_total || imp_cnt || imp_total)
return hab_stat_buffer_print(buf, size,
"ctx %d exp %d size %d imp %d size %d\n",
ctx->owner, exp_cnt, exp_total,
imp_cnt, imp_total);
else
return 0;
}
int hab_stat_show_expimp(struct hab_driver *driver,
int pid, char *buf, int size)
{
struct uhab_context *ctx;
int ret;
ret = strlcpy(buf, "", size);
spin_lock_bh(&hab_driver.drvlock);
list_for_each_entry(ctx, &hab_driver.uctx_list, node) {
if (pid == ctx->owner)
ret = print_ctx_total_expimp(ctx, buf, size);
}
spin_unlock_bh(&hab_driver.drvlock);
return ret;
}

View file

@ -194,6 +194,55 @@ void hab_vchan_stop_notify(struct virtual_channel *vchan)
hab_vchan_stop(vchan);
}
static int hab_vchans_per_pchan_empty(struct physical_channel *pchan)
{
int empty;
read_lock(&pchan->vchans_lock);
empty = list_empty(&pchan->vchannels);
read_unlock(&pchan->vchans_lock);
return empty;
}
static int hab_vchans_empty(int vmid)
{
int i, empty = 1;
struct physical_channel *pchan;
struct hab_device *hab_dev;
for (i = 0; i < hab_driver.ndevices; i++) {
hab_dev = &hab_driver.devp[i];
spin_lock_bh(&hab_dev->pchan_lock);
list_for_each_entry(pchan, &hab_dev->pchannels, node) {
if (pchan->vmid_remote == vmid) {
if (!hab_vchans_per_pchan_empty(pchan)) {
empty = 0;
spin_unlock_bh(&hab_dev->pchan_lock);
break;
}
}
}
spin_unlock_bh(&hab_dev->pchan_lock);
}
return empty;
}
/*
* block until all vchans of a given GVM are explicitly closed
* with habmm_socket_close() by hab clients themselves
*/
void hab_vchans_empty_wait(int vmid)
{
pr_info("waiting for GVM%d's sockets closure\n", vmid);
while (!hab_vchans_empty(vmid))
schedule();
pr_info("all of GVM%d's sockets are closed\n", vmid);
}
int hab_vchan_find_domid(struct virtual_channel *vchan)
{

View file

@ -11,13 +11,12 @@
*
*/
#include "hab.h"
#include "khab_test.h"
#include "hab_pipe.h"
#ifdef CONFIG_MSM_GVM_QUIN
#include "hab_qvm.h"
#endif
#if !defined CONFIG_GHS_VMM && defined(CONFIG_MSM_GVM_QUIN)
#include <asm/cacheflush.h>
#include <linux/list.h>
#include "hab_pipe.h"
#include "hab_qvm.h"
#include "khab_test.h"
static char g_perf_test_result[256];
@ -32,10 +31,8 @@ enum hab_perf_test_type {
static int hab_shmm_throughput_test(void)
{
struct hab_device *habDev;
#ifdef CONFIG_MSM_GVM_QUIN
struct qvm_channel *dev;
#endif
struct hab_shared_buf *sh_buf = NULL;
struct hab_shared_buf *sh_buf;
struct physical_channel *pchan;
struct timeval tv1, tv2;
int i, counter;
@ -56,7 +53,6 @@ static int hab_shmm_throughput_test(void)
pchan = list_first_entry(&(habDev->pchannels),
struct physical_channel, node);
#ifdef CONFIG_MSM_GVM_QUIN
dev = pchan->hyp_data;
if (!dev) {
ret = -EPERM;
@ -64,7 +60,6 @@ static int hab_shmm_throughput_test(void)
}
sh_buf = dev->pipe_ep->tx_info.sh_buf;
#endif
/* pChannel is of 128k, we use 64k to test */
size = 0x10000;
@ -268,3 +263,112 @@ static int get_hab_perf_result(char *buffer, struct kernel_param *kp)
return strlcpy(buffer, g_perf_test_result,
strlen(g_perf_test_result)+1);
}
#endif
static struct kobject *hab_kobject;
static int vchan_stat;
static int context_stat;
static int pid_stat;
static ssize_t vchan_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
return hab_stat_show_vchan(&hab_driver, buf, PAGE_SIZE);
}
static ssize_t vchan_store(struct kobject *kobj, struct kobj_attribute *attr,
char *buf, size_t count)
{
int ret;
ret = sscanf(buf, "%du", &vchan_stat);
if (ret < 1) {
pr_err("failed to read anything from input %d", ret);
return 0;
} else
return vchan_stat;
}
static ssize_t ctx_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
return hab_stat_show_ctx(&hab_driver, buf, PAGE_SIZE);
}
static ssize_t ctx_store(struct kobject *kobj, struct kobj_attribute *attr,
char *buf, size_t count)
{
int ret;
ret = sscanf(buf, "%du", &context_stat);
if (ret < 1) {
pr_err("failed to read anything from input %d", ret);
return 0;
} else
return context_stat;
}
static ssize_t expimp_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
return hab_stat_show_expimp(&hab_driver, pid_stat, buf, PAGE_SIZE);
}
static ssize_t expimp_store(struct kobject *kobj, struct kobj_attribute *attr,
char *buf, size_t count)
{
int ret;
ret = sscanf(buf, "%du", &pid_stat);
if (ret < 1) {
pr_err("failed to read anything from input %d", ret);
return 0;
} else
return pid_stat;
}
static struct kobj_attribute vchan_attribute = __ATTR(vchan_stat, 0660,
vchan_show,
vchan_store);
static struct kobj_attribute ctx_attribute = __ATTR(context_stat, 0660,
ctx_show,
ctx_store);
static struct kobj_attribute expimp_attribute = __ATTR(pid_stat, 0660,
expimp_show,
expimp_store);
int hab_stat_init_sub(struct hab_driver *driver)
{
int result;
hab_kobject = kobject_create_and_add("hab", kernel_kobj);
if (!hab_kobject)
return -ENOMEM;
result = sysfs_create_file(hab_kobject, &vchan_attribute.attr);
if (result)
pr_debug("cannot add vchan in /sys/kernel/hab %d\n", result);
result = sysfs_create_file(hab_kobject, &ctx_attribute.attr);
if (result)
pr_debug("cannot add ctx in /sys/kernel/hab %d\n", result);
result = sysfs_create_file(hab_kobject, &expimp_attribute.attr);
if (result)
pr_debug("cannot add expimp in /sys/kernel/hab %d\n", result);
return result;
}
int hab_stat_deinit_sub(struct hab_driver *driver)
{
sysfs_remove_file(hab_kobject, &vchan_attribute.attr);
sysfs_remove_file(hab_kobject, &ctx_attribute.attr);
sysfs_remove_file(hab_kobject, &expimp_attribute.attr);
kobject_put(hab_kobject);
return 0;
}

View file

@ -13,7 +13,7 @@
#include "hab.h"
#include "hab_qvm.h"
static inline void habhyp_notify(void *commdev)
inline void habhyp_notify(void *commdev)
{
struct qvm_channel *dev = (struct qvm_channel *)commdev;
@ -70,9 +70,14 @@ int physical_channel_send(struct physical_channel *pchan,
struct habmm_xing_vm_stat *pstat =
(struct habmm_xing_vm_stat *)payload;
if (pstat) {
do_gettimeofday(&tv);
pstat->tx_sec = tv.tv_sec;
pstat->tx_usec = tv.tv_usec;
} else {
spin_unlock_bh(&dev->io_lock);
return -EINVAL;
}
}
if (sizebytes) {