Files

263 lines
6.7 KiB
C

/* Copyright (c) 2018-2019, 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,
"nm %s r %d lc %d rm %d sq_t %d sq_r %d st 0x%x vn %d:\n",
pchan->name, pchan->is_be, pchan->vmid_local,
pchan->vmid_remote, pchan->sequence_tx,
pchan->sequence_rx, pchan->status, pchan->vcnt);
read_lock(&pchan->vchans_lock);
list_for_each_entry(vc, &pchan->vchannels, pnode) {
ret = hab_stat_buffer_print(buf, size,
"%08X(%d:%d) ", vc->id,
get_refcnt(vc->refcount),
vc->otherend_closed);
}
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 = NULL;
int exp_total = 0, imp_total = 0;
int exp_cnt = 0, imp_cnt = 0;
struct export_desc *exp = NULL;
int exim_size = 0;
read_lock(&ctx->exp_lock);
hab_stat_buffer_print(buf, size, "export[expid:vcid:size]: ");
list_for_each_entry(exp, &ctx->exp_whse, node) {
pfn_table = (struct compressed_pfns *)exp->payload;
exim_size = get_pft_tbl_total_size(pfn_table);
exp_total += exim_size;
exp_cnt++;
hab_stat_buffer_print(buf, size,
"[%d:%x:%d] ", exp->export_id,
exp->vcid_local, exim_size);
}
hab_stat_buffer_print(buf, size, "\n");
read_unlock(&ctx->exp_lock);
spin_lock_bh(&ctx->imp_lock);
hab_stat_buffer_print(buf, size, "import[expid:vcid:size]: ");
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;
exim_size = get_pft_tbl_total_size(pfn_table);
imp_total += exim_size;
imp_cnt++;
hab_stat_buffer_print(buf, size,
"[%d:%x:%d] ", exp->export_id,
exp->vcid_local, exim_size);
}
}
hab_stat_buffer_print(buf, size, "\n");
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;
}
#define HAB_PIPE_DUMP_FILE_NAME "/sdcard/habpipe-"
#define HAB_PIPE_DUMP_FILE_EXT ".dat"
#define HAB_PIPEDUMP_SIZE (768*1024)
static char *filp;
static int pipedump_idx;
int dump_hab_open(void)
{
int rc = 0;
char file_path[256];
char file_time[100];
rc = dump_hab_get_file_name(file_time, sizeof(file_time));
strlcpy(file_path, HAB_PIPE_DUMP_FILE_NAME, sizeof(file_path));
strlcat(file_path, file_time, sizeof(file_path));
strlcat(file_path, HAB_PIPE_DUMP_FILE_EXT, sizeof(file_path));
filp = vmalloc(HAB_PIPEDUMP_SIZE);
if (IS_ERR(filp)) {
rc = PTR_ERR(filp);
pr_err("failed to create pipe dump buffer rc %d\n", rc);
filp = NULL;
} else {
pr_info("hab pipe dump buffer opened %s\n", file_path);
pipedump_idx = 0;
dump_hab_buf(file_path, strlen(file_path)); /* id first */
}
return rc;
}
void dump_hab_close(void)
{
pr_info("pipe dump content size %d completed\n", pipedump_idx);
/* transfer buffer ownership to devcoredump */
filp = NULL;
pipedump_idx = 0;
}
int dump_hab_buf(void *buf, int size)
{
if (!buf || !size || size > HAB_PIPEDUMP_SIZE - pipedump_idx) {
pr_err("wrong parameters buf %pK size %d allowed %d\n",
buf, size, HAB_PIPEDUMP_SIZE - pipedump_idx);
return 0;
}
memcpy(&filp[pipedump_idx], buf, size);
pipedump_idx += size;
return size;
}
void dump_hab(void)
{
struct physical_channel *pchan = NULL;
int i = 0;
char str[8] = {35, 35, 35, 35, 35, 35, 35, 35}; /* ## */
dump_hab_open();
for (i = 0; i < hab_driver.ndevices; i++) {
struct hab_device *habdev = &hab_driver.devp[i];
/* only care gfx and mis */
if (habdev->id == MM_GFX || habdev->id == MM_MISC) {
list_for_each_entry(pchan, &habdev->pchannels, node) {
if (pchan->vcnt > 0) {
pr_info("***** dump pchan %s vcnt %d *****\n",
pchan->name, pchan->vcnt);
hab_pipe_read_dump(pchan);
}
}
dump_hab_buf(str, 8); /* separator */
}
}
dev_coredumpv(hab_driver.dev, filp, pipedump_idx, GFP_KERNEL);
dump_hab_close();
}