983 lines
20 KiB
C
983 lines
20 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/* Copyright (C) 2021 - 2023, Shanghai Yunsilicon Technology Co., Ltd.
|
|
* All rights reserved.
|
|
*/
|
|
|
|
#include <linux/debugfs.h>
|
|
#include <linux/time.h>
|
|
#include "common/xsc_core.h"
|
|
#include "common/xsc_hsi.h"
|
|
#include "common/driver.h"
|
|
#include "common/qp.h"
|
|
#include "common/cq.h"
|
|
#include "fw/xsc_tbm.h"
|
|
|
|
enum {
|
|
QP_PID,
|
|
QP_STATE,
|
|
QP_XPORT,
|
|
QP_MTU,
|
|
QP_N_RECV,
|
|
QP_RECV_SZ,
|
|
QP_N_SEND,
|
|
QP_LOG_PG_SZ,
|
|
QP_RQPN,
|
|
};
|
|
|
|
static char *qp_fields[] = {
|
|
[QP_PID] = "pid",
|
|
[QP_STATE] = "state",
|
|
[QP_XPORT] = "transport",
|
|
[QP_MTU] = "mtu",
|
|
[QP_N_RECV] = "num_recv",
|
|
[QP_RECV_SZ] = "rcv_wqe_sz",
|
|
[QP_N_SEND] = "num_send",
|
|
[QP_LOG_PG_SZ] = "log2_page_sz",
|
|
[QP_RQPN] = "remote_qpn",
|
|
};
|
|
|
|
enum {
|
|
EQ_NUM_EQES,
|
|
EQ_INTR,
|
|
EQ_LOG_PG_SZ,
|
|
};
|
|
|
|
static char *eq_fields[] = {
|
|
[EQ_NUM_EQES] = "num_eqes",
|
|
[EQ_INTR] = "intr",
|
|
[EQ_LOG_PG_SZ] = "log_page_size",
|
|
};
|
|
|
|
enum {
|
|
CQ_PID,
|
|
CQ_NUM_CQES,
|
|
CQ_LOG_PG_SZ,
|
|
};
|
|
|
|
static char *cq_fields[] = {
|
|
[CQ_PID] = "pid",
|
|
[CQ_NUM_CQES] = "num_cqes",
|
|
[CQ_LOG_PG_SZ] = "log_page_size",
|
|
};
|
|
|
|
struct dentry *xsc_debugfs_root;
|
|
EXPORT_SYMBOL(xsc_debugfs_root);
|
|
|
|
static ssize_t xsc_debugfs_reg_read(struct file *filp, char __user *buffer,
|
|
size_t count, loff_t *ppos)
|
|
{
|
|
char *buf;
|
|
int len;
|
|
char xsc_debugfs_reg_buf[256] = "";
|
|
|
|
/* don't allow partial reads */
|
|
if (*ppos != 0)
|
|
return 0;
|
|
|
|
buf = kasprintf(GFP_KERNEL, "%s: %s\n",
|
|
"xsc debugfs",
|
|
xsc_debugfs_reg_buf);
|
|
if (!buf)
|
|
return -ENOMEM;
|
|
|
|
if (count < strlen(buf)) {
|
|
kfree(buf);
|
|
return -ENOSPC;
|
|
}
|
|
|
|
len = simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf));
|
|
|
|
kfree(buf);
|
|
|
|
return len;
|
|
}
|
|
|
|
static ssize_t xsc_debugfs_reg_write(struct file *filp,
|
|
const char __user *buffer,
|
|
size_t count, loff_t *ppos)
|
|
{
|
|
struct xsc_core_device *xdev = filp->private_data;
|
|
u64 reg;
|
|
int cnt, len;
|
|
int num;
|
|
int offset;
|
|
char xsc_debugfs_reg_buf[256] = "";
|
|
|
|
/* don't allow partial writes */
|
|
if (*ppos != 0)
|
|
return 0;
|
|
|
|
if (count >= sizeof(xsc_debugfs_reg_buf))
|
|
return -ENOSPC;
|
|
|
|
len = simple_write_to_buffer(xsc_debugfs_reg_buf,
|
|
sizeof(xsc_debugfs_reg_buf) - 1,
|
|
ppos, buffer, count);
|
|
if (len < 0)
|
|
return len;
|
|
|
|
xsc_debugfs_reg_buf[len] = '\0';
|
|
|
|
if (strncmp(xsc_debugfs_reg_buf, "write", 5) == 0) {
|
|
cnt = sscanf(&xsc_debugfs_reg_buf[5], "%llx %n",
|
|
®, &offset);
|
|
if (cnt == 1) {
|
|
int tmp;
|
|
int value;
|
|
int buf[8];
|
|
int *ptr;
|
|
|
|
offset += 5;
|
|
num = 0;
|
|
while (1) {
|
|
cnt = sscanf(&xsc_debugfs_reg_buf[offset], "%x %n", &value, &tmp);
|
|
if (cnt < 2)
|
|
break;
|
|
xsc_core_info(xdev, "write: 0x%llx = 0x%x\n",
|
|
(reg + sizeof(int) * num), value);
|
|
offset += tmp;
|
|
buf[num++] = value;
|
|
if (num == 8)
|
|
break;
|
|
}
|
|
if (num > 1) {
|
|
ptr = &buf[0];
|
|
IA_WRITE(xdev, reg, ptr, num);
|
|
} else if (num == 1) {
|
|
REG_WR32(xdev, reg, buf[0]);
|
|
}
|
|
} else {
|
|
xsc_core_err(xdev, "write <reg> <value>\n");
|
|
}
|
|
} else if (strncmp(xsc_debugfs_reg_buf, "read", 4) == 0) {
|
|
cnt = sscanf(&xsc_debugfs_reg_buf[4], "%llx %d %n", ®, &num, &offset);
|
|
if (cnt == 2) {
|
|
int *buf;
|
|
int i;
|
|
int *ptr;
|
|
|
|
buf = kcalloc(num, sizeof(int), GFP_KERNEL);
|
|
if (!buf)
|
|
return -ENOMEM;
|
|
ptr = buf;
|
|
IA_READ(xdev, reg, ptr, num);
|
|
xsc_core_info(xdev, "read: 0x%llx num:%d\n", reg, num);
|
|
for (i = 0; i < num; i++)
|
|
xsc_core_info(xdev, "read:0x%llx = %#x\n",
|
|
(reg + sizeof(int) * i), buf[i]);
|
|
} else if (cnt == 1) {
|
|
int value = REG_RD32(xdev, reg);
|
|
|
|
xsc_core_info(xdev, "read: 0x%llx = %#x\n", reg, value);
|
|
} else {
|
|
xsc_core_err(xdev, "read <reg>\n");
|
|
}
|
|
} else {
|
|
xsc_core_err(xdev, "Unknown command %s\n", xsc_debugfs_reg_buf);
|
|
xsc_core_err(xdev, "Available commands:\n");
|
|
xsc_core_err(xdev, "read <reg>\n");
|
|
xsc_core_err(xdev, "write <reg> <value>\n");
|
|
}
|
|
return count;
|
|
}
|
|
|
|
static const struct file_operations xsc_debugfs_reg_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = simple_open,
|
|
.read = xsc_debugfs_reg_read,
|
|
.write = xsc_debugfs_reg_write,
|
|
};
|
|
|
|
static ssize_t xsc_debugfs_vlan_read(struct file *filp, char __user *buffer,
|
|
size_t count, loff_t *ppos)
|
|
{
|
|
char *buf;
|
|
int len;
|
|
char xsc_debugfs_vlan_buf[256] = "";
|
|
|
|
/* don't allow partial reads */
|
|
if (*ppos != 0)
|
|
return 0;
|
|
|
|
buf = kasprintf(GFP_KERNEL, "%s: %s\n",
|
|
"vlan debugfs", xsc_debugfs_vlan_buf);
|
|
if (!buf)
|
|
return -ENOMEM;
|
|
|
|
if (count < strlen(buf)) {
|
|
kfree(buf);
|
|
return -ENOSPC;
|
|
}
|
|
|
|
len = simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf));
|
|
|
|
kfree(buf);
|
|
return len;
|
|
}
|
|
|
|
static ssize_t xsc_debugfs_vlan_write(struct file *filp,
|
|
const char __user *buffer,
|
|
size_t count, loff_t *ppos)
|
|
{
|
|
struct xsc_core_device *xdev = filp->private_data;
|
|
struct xsc_vlan_config config;
|
|
char proto[16];
|
|
int off = 0;
|
|
int len, cnt;
|
|
char xsc_debugfs_vlan_buf[256] = "";
|
|
|
|
/* don't allow partial writes */
|
|
if (*ppos != 0)
|
|
return 0;
|
|
|
|
if (count >= sizeof(xsc_debugfs_vlan_buf))
|
|
return -ENOSPC;
|
|
|
|
len = simple_write_to_buffer(xsc_debugfs_vlan_buf,
|
|
sizeof(xsc_debugfs_vlan_buf) - 1,
|
|
ppos, buffer, count);
|
|
if (len < 0)
|
|
return len;
|
|
|
|
xsc_debugfs_vlan_buf[len] = '\0';
|
|
memset(&config, 0, sizeof(config));
|
|
/* <mode> <pvlan> <vlan_start> <vlan_num> <proto> <prio> <smac_en>
|
|
* trunk 100 0 4096 802.1q 0 0
|
|
* tunnel 100 0 4096 802.1ad 1 0
|
|
*/
|
|
if (strncmp(xsc_debugfs_vlan_buf, "trunk", 5) == 0) {
|
|
off = 5;
|
|
config.mode = XSC_VLAN_MODE_TRUNK;
|
|
} else if (strncmp(xsc_debugfs_vlan_buf, "tunnel", 6) == 0) {
|
|
off = 6;
|
|
config.mode = XSC_VLAN_MODE_TUNNEL;
|
|
} else if (strncmp(xsc_debugfs_vlan_buf, "access", 6) == 0) {
|
|
off = 6;
|
|
config.mode = XSC_VLAN_MODE_ACCESS;
|
|
} else if (strncmp(xsc_debugfs_vlan_buf, "tagged", 6) == 0) {
|
|
off = 6;
|
|
config.mode = XSC_VLAN_MODE_NATIVE_TAGGED;
|
|
} else if (strncmp(xsc_debugfs_vlan_buf, "untagged", 8) == 0) {
|
|
off = 8;
|
|
config.mode = XSC_VLAN_MODE_NATIVE_UNTAGGED;
|
|
} else if (strncmp(xsc_debugfs_vlan_buf, "none", 4) == 0) {
|
|
off = 4;
|
|
config.mode = 0;
|
|
} else {
|
|
xsc_core_err(xdev, "invalid vlan mode: %s\n", xsc_debugfs_vlan_buf);
|
|
return 0;
|
|
}
|
|
|
|
cnt = sscanf(&xsc_debugfs_vlan_buf[off], "%u %u %u %s %u %u",
|
|
&config.pvid, &config.vid_allow_base,
|
|
&config.vid_allow_num, proto,
|
|
&config.prio, &config.smac_filter_en);
|
|
if (cnt < 3) {
|
|
xsc_core_err(xdev, "error arguments: <mode> <vid> <vlan_start> <vlan_num> <proto> <prio> <smac_en>\n");
|
|
return 0;
|
|
}
|
|
|
|
if (strncmp(proto, "802.1q", 6) == 0)
|
|
config.proto = ETH_P_8021Q;
|
|
else if (strncmp(proto, "802.1ad", 7) == 0)
|
|
config.proto = ETH_P_8021AD;
|
|
else
|
|
config.proto = ETH_P_8021Q;
|
|
|
|
if (config.prio > 7) {
|
|
xsc_core_err(xdev, "invalid vlan prio: %s\n", xsc_debugfs_vlan_buf);
|
|
return 0;
|
|
}
|
|
|
|
xsc_core_info(xdev, "%s: vlan_mode=%d vid=%d vlan_allow=%d/%d proto=0x%x prio=%d smac_en=%d",
|
|
__func__, config.mode, config.pvid, config.vid_allow_base,
|
|
config.vid_allow_num, config.proto, config.prio,
|
|
config.smac_filter_en);
|
|
|
|
return count;
|
|
}
|
|
|
|
static const struct file_operations xsc_debugfs_vlan_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = simple_open,
|
|
.read = xsc_debugfs_vlan_read,
|
|
.write = xsc_debugfs_vlan_write,
|
|
};
|
|
|
|
int xsc_vlan_debugfs_init(struct xsc_core_device *dev)
|
|
{
|
|
struct dentry *pfile;
|
|
|
|
if (dev->dev_res->dbg_root) {
|
|
pfile = debugfs_create_file("vlan", 0644,
|
|
dev->dev_res->dbg_root, dev,
|
|
&xsc_debugfs_vlan_fops);
|
|
if (!pfile)
|
|
xsc_core_err(dev, "failed to create vlan debugfs\n");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int xsc_debugfs_init(struct xsc_core_device *dev)
|
|
{
|
|
const char *name = pci_name(dev->pdev);
|
|
struct dentry *pfile;
|
|
|
|
if (!xsc_debugfs_root)
|
|
return -ENOMEM;
|
|
|
|
dev->dev_res->dbg_root = debugfs_create_dir(name, xsc_debugfs_root);
|
|
if (dev->dev_res->dbg_root) {
|
|
pfile = debugfs_create_file("reg_ops", 0600,
|
|
dev->dev_res->dbg_root, dev,
|
|
&xsc_debugfs_reg_fops);
|
|
if (!pfile)
|
|
xsc_core_err(dev, "failed to create debugfs ops for %s\n", name);
|
|
} else {
|
|
xsc_core_err(dev, "failed to create debugfs dir for %s\n", name);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
xsc_vlan_debugfs_init(dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void xsc_debugfs_fini(struct xsc_core_device *dev)
|
|
{
|
|
if (!xsc_debugfs_root)
|
|
return;
|
|
|
|
debugfs_remove_recursive(dev->dev_res->dbg_root);
|
|
}
|
|
|
|
void xsc_register_debugfs(void)
|
|
{
|
|
xsc_debugfs_root = debugfs_create_dir("xsc_pci", NULL);
|
|
}
|
|
|
|
void xsc_unregister_debugfs(void)
|
|
{
|
|
debugfs_remove(xsc_debugfs_root);
|
|
}
|
|
|
|
int xsc_qp_debugfs_init(struct xsc_core_device *dev)
|
|
{
|
|
if (!xsc_debugfs_root)
|
|
return 0;
|
|
|
|
atomic_set(&dev->num_qps, 0);
|
|
|
|
dev->dev_res->qp_debugfs = debugfs_create_dir("QPs", dev->dev_res->dbg_root);
|
|
if (!dev->dev_res->qp_debugfs)
|
|
return -ENOMEM;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void xsc_qp_debugfs_cleanup(struct xsc_core_device *dev)
|
|
{
|
|
if (!xsc_debugfs_root)
|
|
return;
|
|
|
|
debugfs_remove_recursive(dev->dev_res->qp_debugfs);
|
|
}
|
|
|
|
int xsc_eq_debugfs_init(struct xsc_core_device *dev)
|
|
{
|
|
if (!xsc_debugfs_root)
|
|
return 0;
|
|
|
|
dev->dev_res->eq_debugfs = debugfs_create_dir("EQs", dev->dev_res->dbg_root);
|
|
if (!dev->dev_res->eq_debugfs)
|
|
return -ENOMEM;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void xsc_eq_debugfs_cleanup(struct xsc_core_device *dev)
|
|
{
|
|
if (!xsc_debugfs_root)
|
|
return;
|
|
|
|
debugfs_remove_recursive(dev->dev_res->eq_debugfs);
|
|
}
|
|
|
|
static ssize_t average_read(struct file *filp, char __user *buf, size_t count,
|
|
loff_t *pos)
|
|
{
|
|
struct xsc_cmd_stats *stats;
|
|
u64 field = 0;
|
|
int ret;
|
|
int err;
|
|
char tbuf[22];
|
|
|
|
if (*pos)
|
|
return 0;
|
|
|
|
stats = filp->private_data;
|
|
spin_lock(&stats->lock);
|
|
if (stats->n)
|
|
field = stats->sum / stats->n;
|
|
spin_unlock(&stats->lock);
|
|
ret = snprintf(tbuf, sizeof(tbuf), "%llu\n", field);
|
|
if (ret > 0) {
|
|
err = copy_to_user(buf, tbuf, ret);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
*pos += ret;
|
|
return ret;
|
|
}
|
|
|
|
static ssize_t average_write(struct file *filp, const char __user *buf,
|
|
size_t count, loff_t *pos)
|
|
{
|
|
struct xsc_cmd_stats *stats;
|
|
|
|
stats = filp->private_data;
|
|
spin_lock(&stats->lock);
|
|
stats->sum = 0;
|
|
stats->n = 0;
|
|
spin_unlock(&stats->lock);
|
|
|
|
*pos += count;
|
|
|
|
return count;
|
|
}
|
|
|
|
static const struct file_operations stats_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = simple_open,
|
|
.read = average_read,
|
|
.write = average_write,
|
|
};
|
|
|
|
int xsc_cmdif_debugfs_init(struct xsc_core_device *xdev)
|
|
{
|
|
struct xsc_cmd_stats *stats;
|
|
struct xsc_cmd *cmd;
|
|
struct dentry **cmdif_debugfs;
|
|
const char *namep;
|
|
int err;
|
|
int i;
|
|
|
|
if (!xsc_debugfs_root)
|
|
return 0;
|
|
|
|
cmd = &xdev->cmd;
|
|
cmdif_debugfs = &xdev->dev_res->cmdif_debugfs;
|
|
*cmdif_debugfs = debugfs_create_dir("commands", xdev->dev_res->dbg_root);
|
|
if (!*cmdif_debugfs)
|
|
return -ENOMEM;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(cmd->stats); i++) {
|
|
stats = &cmd->stats[i];
|
|
namep = xsc_command_str(i);
|
|
if (strcmp(namep, "unknown command opcode")) {
|
|
stats->root = debugfs_create_dir(namep, *cmdif_debugfs);
|
|
if (!stats->root) {
|
|
xsc_core_warn(xdev, "failed adding command %d\n", i);
|
|
err = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
stats->avg = debugfs_create_file("average", 0400,
|
|
stats->root, stats,
|
|
&stats_fops);
|
|
if (!stats->avg) {
|
|
xsc_core_warn(xdev, "failed creating debugfs file\n");
|
|
err = -ENOMEM;
|
|
goto out;
|
|
}
|
|
|
|
debugfs_create_u64("n", 0400, stats->root, &stats->n);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
out:
|
|
debugfs_remove_recursive(xdev->dev_res->cmdif_debugfs);
|
|
return err;
|
|
}
|
|
|
|
void xsc_cmdif_debugfs_cleanup(struct xsc_core_device *xdev)
|
|
{
|
|
if (!xsc_debugfs_root)
|
|
return;
|
|
|
|
debugfs_remove_recursive(xdev->dev_res->cmdif_debugfs);
|
|
}
|
|
|
|
int xsc_cq_debugfs_init(struct xsc_core_device *dev)
|
|
{
|
|
if (!xsc_debugfs_root)
|
|
return 0;
|
|
|
|
dev->dev_res->cq_debugfs = debugfs_create_dir("CQs", dev->dev_res->dbg_root);
|
|
if (!dev->dev_res->cq_debugfs)
|
|
return -ENOMEM;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void xsc_cq_debugfs_cleanup(struct xsc_core_device *dev)
|
|
{
|
|
if (!xsc_debugfs_root)
|
|
return;
|
|
|
|
debugfs_remove_recursive(dev->dev_res->cq_debugfs);
|
|
}
|
|
|
|
int xsc_qptrace_debugfs_init(struct xsc_core_device *dev)
|
|
{
|
|
if (!xsc_debugfs_root)
|
|
return 0;
|
|
|
|
dev->dev_res->qptrace_debugfs =
|
|
debugfs_create_dir("QPTrace", dev->dev_res->dbg_root);
|
|
if (!dev->dev_res->qptrace_debugfs)
|
|
return -ENOMEM;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void xsc_qptrace_debugfs_cleanup(struct xsc_core_device *dev)
|
|
{
|
|
if (!xsc_debugfs_root)
|
|
return;
|
|
|
|
debugfs_remove_recursive(dev->dev_res->qptrace_debugfs);
|
|
}
|
|
|
|
static u64 qp_read_field(struct xsc_core_device *dev, struct xsc_core_qp *qp,
|
|
int index)
|
|
{
|
|
struct xsc_query_qp_mbox_out *out;
|
|
struct xsc_qp_context *ctx;
|
|
u64 param = 0;
|
|
int err;
|
|
|
|
out = kzalloc(sizeof(*out), GFP_KERNEL);
|
|
if (!out)
|
|
return param;
|
|
|
|
err = xsc_core_qp_query(dev, qp, out, sizeof(*out));
|
|
if (err) {
|
|
xsc_core_warn(dev, "failed to query qp\n");
|
|
goto out;
|
|
}
|
|
|
|
ctx = &out->ctx;
|
|
switch (index) {
|
|
case QP_PID:
|
|
param = qp->pid;
|
|
break;
|
|
case QP_MTU:
|
|
param = ctx->mtu_mode ? IB_MTU_1024 : IB_MTU_4096;
|
|
break;
|
|
case QP_RQPN:
|
|
param = cpu_to_be32(ctx->remote_qpn) & 0xffffff;
|
|
break;
|
|
}
|
|
|
|
out:
|
|
kfree(out);
|
|
return param;
|
|
}
|
|
|
|
static u64 eq_read_field(struct xsc_core_device *dev, struct xsc_eq *eq,
|
|
int index)
|
|
{
|
|
struct xsc_query_eq_mbox_out *out;
|
|
struct xsc_eq_context *ctx;
|
|
u64 param = 0;
|
|
int err;
|
|
|
|
out = kzalloc(sizeof(*out), GFP_KERNEL);
|
|
if (!out)
|
|
return param;
|
|
|
|
ctx = &out->ctx;
|
|
|
|
err = xsc_core_eq_query(dev, eq, out, sizeof(*out));
|
|
if (err) {
|
|
xsc_core_warn(dev, "failed to query eq\n");
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
kfree(out);
|
|
return param;
|
|
}
|
|
|
|
static u64 cq_read_field(struct xsc_core_device *dev, struct xsc_core_cq *cq,
|
|
int index)
|
|
{
|
|
struct xsc_query_cq_mbox_out *out;
|
|
struct xsc_cq_context *ctx;
|
|
u64 param = 0;
|
|
int err;
|
|
|
|
out = kzalloc(sizeof(*out), GFP_KERNEL);
|
|
if (!out)
|
|
return param;
|
|
|
|
ctx = &out->ctx;
|
|
|
|
err = xsc_core_query_cq(dev, cq, out);
|
|
if (err) {
|
|
xsc_core_warn(dev, "failed to query cq\n");
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
kfree(out);
|
|
return param;
|
|
}
|
|
|
|
static ssize_t dbg_read(struct file *filp, char __user *buf, size_t count,
|
|
loff_t *pos)
|
|
{
|
|
struct xsc_field_desc *desc;
|
|
struct xsc_rsc_debug *d;
|
|
char tbuf[18];
|
|
u64 field;
|
|
int ret;
|
|
int err;
|
|
|
|
if (*pos)
|
|
return 0;
|
|
|
|
desc = filp->private_data;
|
|
d = (void *)(desc - desc->i) - sizeof(*d);
|
|
switch (d->type) {
|
|
case XSC_DBG_RSC_QP:
|
|
field = qp_read_field(d->xdev, d->object, desc->i);
|
|
break;
|
|
|
|
case XSC_DBG_RSC_EQ:
|
|
field = eq_read_field(d->xdev, d->object, desc->i);
|
|
break;
|
|
|
|
case XSC_DBG_RSC_CQ:
|
|
field = cq_read_field(d->xdev, d->object, desc->i);
|
|
break;
|
|
|
|
default:
|
|
xsc_core_warn(d->xdev, "invalid resource type %d\n", d->type);
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = snprintf(tbuf, sizeof(tbuf), "0x%llx\n", field);
|
|
if (ret > 0) {
|
|
err = copy_to_user(buf, tbuf, ret);
|
|
if (err)
|
|
return err;
|
|
}
|
|
|
|
*pos += ret;
|
|
return ret;
|
|
}
|
|
|
|
static const struct file_operations fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = simple_open,
|
|
.read = dbg_read,
|
|
};
|
|
|
|
static int add_res_tree(struct xsc_core_device *dev, enum dbg_rsc_type type,
|
|
struct dentry *root, struct xsc_rsc_debug **dbg,
|
|
int rsn, char **field, int nfile, void *data)
|
|
{
|
|
struct xsc_rsc_debug *d;
|
|
char resn[32];
|
|
int err;
|
|
int i;
|
|
|
|
d = kzalloc(sizeof(*d) + nfile * sizeof(d->fields[0]), GFP_KERNEL);
|
|
if (!d)
|
|
return -ENOMEM;
|
|
|
|
d->xdev = dev;
|
|
d->object = data;
|
|
d->type = type;
|
|
sprintf(resn, "0x%x", rsn);
|
|
d->root = debugfs_create_dir(resn, root);
|
|
if (!d->root) {
|
|
err = -ENOMEM;
|
|
goto out_free;
|
|
}
|
|
|
|
for (i = 0; i < nfile; i++) {
|
|
d->fields[i].i = i;
|
|
d->fields[i].dent = debugfs_create_file(field[i], 0400,
|
|
d->root, &d->fields[i],
|
|
&fops);
|
|
if (!d->fields[i].dent) {
|
|
err = -ENOMEM;
|
|
goto out_rem;
|
|
}
|
|
}
|
|
*dbg = d;
|
|
|
|
return 0;
|
|
out_rem:
|
|
debugfs_remove_recursive(d->root);
|
|
|
|
out_free:
|
|
kfree(d);
|
|
return err;
|
|
}
|
|
|
|
static void rem_res_tree(struct xsc_rsc_debug *d)
|
|
{
|
|
debugfs_remove_recursive(d->root);
|
|
kfree(d);
|
|
}
|
|
|
|
int xsc_debug_qp_add(struct xsc_core_device *dev, struct xsc_core_qp *qp)
|
|
{
|
|
int err;
|
|
|
|
if (!xsc_debugfs_root)
|
|
return 0;
|
|
|
|
err = add_res_tree(dev, XSC_DBG_RSC_QP, dev->dev_res->qp_debugfs,
|
|
&qp->dbg, qp->qpn, qp_fields,
|
|
ARRAY_SIZE(qp_fields), qp);
|
|
if (err)
|
|
qp->dbg = NULL;
|
|
|
|
return err;
|
|
}
|
|
|
|
void xsc_debug_qp_remove(struct xsc_core_device *dev, struct xsc_core_qp *qp)
|
|
{
|
|
if (!xsc_debugfs_root)
|
|
return;
|
|
|
|
if (qp->dbg)
|
|
rem_res_tree(qp->dbg);
|
|
}
|
|
|
|
static int set_udp_sport(u32 qpn, u32 sport, struct xsc_core_device *xdev, struct xsc_qp_trace *t)
|
|
{
|
|
int err;
|
|
struct xsc_ap_feat_mbox_in in;
|
|
struct xsc_ap_feat_mbox_out out;
|
|
struct timespec64 ts;
|
|
struct xsc_qpt_update_msg msg;
|
|
|
|
ktime_get_boottime_ts64(&ts);
|
|
|
|
memset(&in, 0, sizeof(in));
|
|
memset(&out, 0, sizeof(out));
|
|
|
|
in.hdr.opcode = __cpu_to_be16(XSC_CMD_OP_AP_FEAT);
|
|
in.xsc_ap_feature_opcode = __cpu_to_be16(XSC_AP_FEAT_SET_UDP_SPORT);
|
|
in.ap.set_udp_sport.qpn = __cpu_to_be32(qpn);
|
|
in.ap.set_udp_sport.udp_sport = __cpu_to_be32(sport);
|
|
|
|
err = xsc_cmd_exec(xdev, (void *)&in, sizeof(in), (void *)&out, sizeof(out));
|
|
if (err || out.hdr.status) {
|
|
xsc_core_err(xdev, "Failed to set udp_sport, err(%u), status(%u)\n", err,
|
|
out.hdr.status);
|
|
return -EINVAL;
|
|
}
|
|
|
|
msg.main_ver = YS_QPTRACE_VER_MAJOR;
|
|
msg.sub_ver = YS_QPTRACE_VER_MINOR;
|
|
msg.type = YS_QPTRACE_UPDATE_TYPE_SPORT;
|
|
msg.data.timestamp = (u64)(u32)ts.tv_sec * MSEC_PER_SEC +
|
|
ts.tv_nsec / NSEC_PER_MSEC;
|
|
msg.data.qpn = qpn;
|
|
msg.data.bus = xdev->bus_num;
|
|
msg.data.dev = xdev->dev_num;
|
|
msg.data.fun = xdev->func_id;
|
|
msg.data.update.sport.port_old = t->s_port;
|
|
msg.data.update.sport.port_new = __cpu_to_be16(sport);
|
|
t->s_port = msg.data.update.sport.port_new;
|
|
|
|
qpts_write_one_msg(&msg);
|
|
|
|
xsc_core_info(xdev, "Set qpn(%u) udp_sport(%u)\n", qpn, sport);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static ssize_t trace_read(struct file *filp, char __user *buf, size_t count, loff_t *pos)
|
|
{
|
|
struct xsc_core_qp *qp = filp->private_data;
|
|
struct xsc_qp_trace *trace_info;
|
|
int err;
|
|
int len;
|
|
|
|
if (*pos)
|
|
return 0;
|
|
|
|
if (!qp || !qp->trace_info)
|
|
return -EIO;
|
|
|
|
trace_info = qp->trace_info;
|
|
|
|
len = sizeof(struct xsc_qp_trace);
|
|
err = copy_to_user(buf, trace_info, len);
|
|
if (err)
|
|
return err;
|
|
|
|
*pos += len;
|
|
return len;
|
|
}
|
|
|
|
static ssize_t trace_write(struct file *filp, const char __user *buf, size_t count, loff_t *pos)
|
|
{
|
|
struct xsc_core_qp *qp = filp->private_data;
|
|
struct xsc_qp_trace *trace_info;
|
|
struct xsc_core_device *xdev;
|
|
int ret = 0, len;
|
|
u32 sport;
|
|
char tmp_buf[256] = "";
|
|
|
|
ret = -EIO;
|
|
if (!qp || !qp->dbg || !qp->dbg->xdev || !qp->trace_info) {
|
|
pr_err("%s error null pointer!\n", __func__);
|
|
goto trace_write_out;
|
|
}
|
|
|
|
trace_info = qp->trace_info;
|
|
xdev = qp->dbg->xdev;
|
|
|
|
ret = 0;
|
|
/* don't allow partial writes */
|
|
if (*pos != 0) {
|
|
xsc_core_err(xdev, "Don't allow partial writes!\n");
|
|
goto trace_write_out;
|
|
}
|
|
|
|
ret = -ENOSPC;
|
|
if (count >= sizeof(tmp_buf)) {
|
|
xsc_core_err(xdev, "Count out of size of buffer!\n");
|
|
goto trace_write_out;
|
|
}
|
|
|
|
len = simple_write_to_buffer(tmp_buf, sizeof(tmp_buf) - 1,
|
|
pos, buf, count);
|
|
ret = len;
|
|
if (len < 0) {
|
|
xsc_core_err(xdev, "Write to buffer error(%d)!\n", len);
|
|
goto trace_write_out;
|
|
}
|
|
|
|
tmp_buf[len] = '\0';
|
|
|
|
// <sport>
|
|
// sport 10000
|
|
if (strncmp(tmp_buf, "sport", 5) == 0) {
|
|
ret = kstrtouint(&tmp_buf[6], 0, &sport);
|
|
if (ret != 0) {
|
|
xsc_core_err(xdev, "error arguments: <sport>\n");
|
|
ret = -EINVAL;
|
|
goto trace_write_out;
|
|
}
|
|
ret = set_udp_sport(trace_info->lqpn, sport, xdev, trace_info);
|
|
if (ret) {
|
|
ret = -EIO;
|
|
goto trace_write_out;
|
|
}
|
|
} else {
|
|
xsc_core_err(xdev, "invalid arguments: %s\n", tmp_buf);
|
|
ret = -EOPNOTSUPP;
|
|
goto trace_write_out;
|
|
}
|
|
|
|
return count;
|
|
|
|
trace_write_out:
|
|
return ret;
|
|
}
|
|
|
|
static const struct file_operations fops_trace = {
|
|
.owner = THIS_MODULE,
|
|
.open = simple_open,
|
|
.read = trace_read,
|
|
.write = trace_write,
|
|
};
|
|
|
|
int xsc_create_qptrace(struct xsc_core_device *dev, struct xsc_core_qp *qp)
|
|
{
|
|
char name[16];
|
|
|
|
if (!xsc_debugfs_root)
|
|
return 0;
|
|
|
|
snprintf(name, sizeof(name), "%d", qp->qpn);
|
|
|
|
qp->trace = debugfs_create_file(name, 0644, dev->dev_res->qptrace_debugfs,
|
|
(void *)qp, &fops_trace);
|
|
if (!qp->trace)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void xsc_remove_qptrace(struct xsc_core_device *dev, struct xsc_core_qp *qp)
|
|
{
|
|
if (!xsc_debugfs_root)
|
|
return;
|
|
|
|
debugfs_remove(qp->trace);
|
|
}
|
|
|
|
int xsc_debug_eq_add(struct xsc_core_device *dev, struct xsc_eq *eq)
|
|
{
|
|
int err;
|
|
|
|
if (!xsc_debugfs_root)
|
|
return 0;
|
|
|
|
err = add_res_tree(dev, XSC_DBG_RSC_EQ, dev->dev_res->eq_debugfs,
|
|
&eq->dbg, eq->eqn, eq_fields,
|
|
ARRAY_SIZE(eq_fields), eq);
|
|
if (err)
|
|
eq->dbg = NULL;
|
|
|
|
return err;
|
|
}
|
|
|
|
void xsc_debug_eq_remove(struct xsc_core_device *dev, struct xsc_eq *eq)
|
|
{
|
|
if (!xsc_debugfs_root)
|
|
return;
|
|
|
|
if (eq->dbg)
|
|
rem_res_tree(eq->dbg);
|
|
}
|
|
|
|
int xsc_debug_cq_add(struct xsc_core_device *dev, struct xsc_core_cq *cq)
|
|
{
|
|
int err;
|
|
|
|
if (!xsc_debugfs_root)
|
|
return 0;
|
|
|
|
err = add_res_tree(dev, XSC_DBG_RSC_CQ, dev->dev_res->cq_debugfs,
|
|
&cq->dbg, cq->cqn, cq_fields,
|
|
ARRAY_SIZE(cq_fields), cq);
|
|
if (err)
|
|
cq->dbg = NULL;
|
|
|
|
return err;
|
|
}
|
|
|
|
void xsc_debug_cq_remove(struct xsc_core_device *dev, struct xsc_core_cq *cq)
|
|
{
|
|
if (!xsc_debugfs_root)
|
|
return;
|
|
|
|
if (cq->dbg)
|
|
rem_res_tree(cq->dbg);
|
|
}
|