[<prev] [next>] [day] [month] [year] [list]
Message-ID:
<MEYP282MB2697F01D8438D2455E8A7DEBBB08A@MEYP282MB2697.AUSP282.PROD.OUTLOOK.COM>
Date: Thu, 3 Aug 2023 10:58:00 +0800
From: Jinjian Song <songjinjian@...mail.com>
To: netdev@...r.kernel.org
Cc: kuba@...nel.org,
davem@...emloft.net,
johannes@...solutions.net,
ryazanov.s.a@...il.com,
loic.poulain@...aro.org,
ilpo.jarvinen@...ux.intel.com,
ricardo.martinez@...ux.intel.com,
chiranjeevi.rapolu@...ux.intel.com,
haijun.liu@...iatek.com,
edumazet@...gle.com,
pabeni@...hat.com,
chandrashekar.devegowda@...el.com,
m.chetan.kumar@...ux.intel.com,
linuxwwan@...el.com,
linuxwwan_5g@...el.com,
soumya.prakash.mishra@...el.com,
jesse.brandeburg@...el.com,
danielwinkler@...gle.com,
Jinjian Song <jinjian.song@...ocom.com>
Subject: [net-next 4/6] net: wwan: t7xx: Creates region & snapshot for coredump log collection
From: Jinjian Song <jinjian.song@...ocom.com>
Adds support for t7xx wwan device coredump collection using devlink.
In case of coredump collection when wwan device encounters an exception
it reboots & stays in fastboot mode for coredump collection by host driver.
On detecting exception state driver collects the core dump, creates the
devlink region & reports an event to user space application for dump
collection. The user space application invokes devlink region read command
for dump collection.
Below are the devlink commands used for coredump collection.
devlink region new pci/$BDF/mr_dump
devlink region read pci/$BDF/mr_dump snapshot $ID address $ADD length $LEN
devlink region del pci/$BDF/mr_dump snapshot $ID
Base on the v5 patch version of follown series:
'net: wwan: t7xx: fw flashing & coredump support'
(https://patchwork.kernel.org/project/netdevbpf/patch/fc8bbb0b66a5ff3a489ea9857d79b374508090ef.1674307425.git.m.chetan.kumar@linux.intel.com/)
Signed-off-by: Jinjian Song <jinjian.song@...ocom.com>
---
drivers/net/wwan/t7xx/t7xx_port_devlink.c | 243 +++++++++++++++++++++
drivers/net/wwan/t7xx/t7xx_port_devlink.h | 41 +++-
drivers/net/wwan/t7xx/t7xx_state_monitor.c | 2 +
3 files changed, 285 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wwan/t7xx/t7xx_port_devlink.c b/drivers/net/wwan/t7xx/t7xx_port_devlink.c
index f10804a2c0d7..0949809aa219 100644
--- a/drivers/net/wwan/t7xx/t7xx_port_devlink.c
+++ b/drivers/net/wwan/t7xx/t7xx_port_devlink.c
@@ -9,6 +9,11 @@
#include "t7xx_port_ap_msg.h"
#include "t7xx_port_devlink.h"
+static struct t7xx_devlink_region_info t7xx_devlink_region_infos[] = {
+ [T7XX_MRDUMP_INDEX] = {"mr_dump", T7XX_MRDUMP_SIZE},
+ [T7XX_LKDUMP_INDEX] = {"lk_dump", T7XX_LKDUMP_SIZE},
+};
+
static int t7xx_devlink_port_read(struct t7xx_port *port, char *buf, size_t count)
{
struct sk_buff *skb;
@@ -177,6 +182,149 @@ static int t7xx_devlink_fb_flash_partition(struct t7xx_port *port, const char *p
return t7xx_devlink_fb_flash(port, partition);
}
+static int t7xx_devlink_fb_cmd_send(struct t7xx_port *port, char *cmd)
+{
+ int len = strlen(cmd);
+ int ret;
+
+ ret = t7xx_devlink_port_write(port, cmd, len);
+ if (ret == len)
+ return 0;
+
+ return ret;
+}
+
+static int t7xx_devlink_fb_get_core(struct t7xx_port *port)
+{
+ u32 mrd_mb = T7XX_MRDUMP_SIZE / (1024 * 1024);
+ struct t7xx_devlink *dl = port->t7xx_dev->dl;
+ char mcmd[T7XX_FB_MCMD_SIZE + 1];
+ size_t offset_dlen = 0;
+ int clen, dlen, ret;
+
+ dl->regions[T7XX_MRDUMP_INDEX].buf = vmalloc(dl->regions[T7XX_MRDUMP_INDEX].info->size);
+ if (!dl->regions[T7XX_MRDUMP_INDEX].buf)
+ return -ENOMEM;
+
+ set_bit(T7XX_MRDUMP_STATUS, &dl->status);
+ ret = t7xx_devlink_fb_raw_command(T7XX_FB_CMD_OEM_MRDUMP, port, NULL);
+ if (ret) {
+ dev_err(port->dev, "%s command failed\n", T7XX_FB_CMD_OEM_MRDUMP);
+ goto free_mem;
+ }
+
+ while (dl->regions[T7XX_MRDUMP_INDEX].info->size > offset_dlen) {
+ clen = t7xx_devlink_port_read(port, mcmd, sizeof(mcmd) - 1);
+ if (clen <= 0)
+ goto free_mem;
+
+ mcmd[clen] = '\0';
+ if (!strcmp(mcmd, T7XX_FB_CMD_RTS)) {
+ memset(mcmd, 0, sizeof(mcmd));
+ ret = t7xx_devlink_fb_cmd_send(port, T7XX_FB_CMD_CTS);
+ if (ret < 0) {
+ dev_err(port->dev, "write for _CTS failed:%zu\n",
+ strlen(T7XX_FB_CMD_CTS));
+ goto free_mem;
+ }
+
+ dlen = t7xx_devlink_port_read(port, dl->regions[T7XX_MRDUMP_INDEX].buf +
+ offset_dlen, T7XX_FB_MDATA_SIZE);
+ if (dlen <= 0) {
+ dev_err(port->dev, "read data error(%d)\n", dlen);
+ ret = dlen;
+ goto free_mem;
+ }
+ offset_dlen += dlen;
+
+ ret = t7xx_devlink_fb_cmd_send(port, T7XX_FB_CMD_FIN);
+ if (ret < 0) {
+ dev_err(port->dev, "_FIN failed, (Read %05zu:%05zu)\n",
+ strlen(T7XX_FB_CMD_FIN), offset_dlen);
+ goto free_mem;
+ }
+ continue;
+ } else if (!strcmp(mcmd, T7XX_FB_RESP_MRDUMP_DONE)) {
+ dev_dbg(port->dev, "%s! size:%zd\n", T7XX_FB_RESP_MRDUMP_DONE, offset_dlen);
+ clear_bit(T7XX_MRDUMP_STATUS, &dl->status);
+ return 0;
+ }
+ dev_err(port->dev, "getcore protocol error (read len %05d, response %s)\n",
+ clen, mcmd);
+ ret = -EPROTO;
+ goto free_mem;
+ }
+
+ dev_err(port->dev, "mrdump exceeds %uMB size. Discarded!\n", mrd_mb);
+
+free_mem:
+ vfree(dl->regions[T7XX_MRDUMP_INDEX].buf);
+ clear_bit(T7XX_MRDUMP_STATUS, &dl->status);
+ return ret;
+}
+
+static int t7xx_devlink_fb_dump_log(struct t7xx_port *port)
+{
+ struct t7xx_devlink *dl = port->t7xx_dev->dl;
+ struct t7xx_devlink_region *lkdump_region;
+ char rsp[T7XX_FB_RESPONSE_SIZE];
+ int datasize = 0, ret;
+ size_t offset = 0;
+
+ if (dl->status != T7XX_DEVLINK_IDLE) {
+ dev_err(&dl->t7xx_dev->pdev->dev, "Modem is busy!\n");
+ return -EBUSY;
+ }
+
+ set_bit(T7XX_LKDUMP_STATUS, &dl->status);
+ ret = t7xx_devlink_fb_raw_command(T7XX_FB_CMD_OEM_LKDUMP, port, rsp);
+ if (ret) {
+ dev_err(port->dev, "%s command returns failure\n", T7XX_FB_CMD_OEM_LKDUMP);
+ goto err_clear_bit;
+ }
+
+ ret = kstrtoint(rsp, 16, &datasize);
+ if (ret) {
+ dev_err(port->dev, "bad value\n");
+ goto err_clear_bit;
+ }
+
+ lkdump_region = &dl->regions[T7XX_LKDUMP_INDEX];
+ if (datasize > lkdump_region->info->size) {
+ dev_err(port->dev, "lkdump size is more than %dKB. Discarded!\n",
+ T7XX_LKDUMP_SIZE / 1024);
+ ret = -EFBIG;
+ goto err_clear_bit;
+ }
+
+ lkdump_region->buf = vmalloc(lkdump_region->info->size);
+ if (!lkdump_region->buf) {
+ ret = -ENOMEM;
+ goto err_clear_bit;
+ }
+
+ while (datasize > 0) {
+ int dlen = t7xx_devlink_port_read(port, lkdump_region->buf + offset, datasize);
+
+ if (dlen <= 0) {
+ dev_err(port->dev, "lkdump read error ret = %d\n", dlen);
+ ret = dlen;
+ goto err_clear_bit;
+ }
+
+ datasize -= dlen;
+ offset += dlen;
+ }
+
+ dev_dbg(port->dev, "LKDUMP DONE! size:%zd\n", offset);
+ clear_bit(T7XX_LKDUMP_STATUS, &dl->status);
+ return t7xx_devlink_fb_handle_response(port, NULL);
+
+err_clear_bit:
+ clear_bit(T7XX_LKDUMP_STATUS, &dl->status);
+ return ret;
+}
+
static int t7xx_devlink_flash_update(struct devlink *devlink,
struct devlink_flash_update_params *params,
struct netlink_ext_ack *extack)
@@ -387,6 +535,65 @@ static const struct devlink_ops devlink_flash_ops = {
.reload_up = t7xx_devlink_reload_up,
};
+static int t7xx_devlink_region_snapshot(struct devlink *dl, const struct devlink_region_ops *ops,
+ struct netlink_ext_ack *extack, u8 **data)
+{
+ struct t7xx_devlink *t7xx_dl = devlink_priv(dl);
+ struct t7xx_devlink_region *region = ops->priv;
+ struct t7xx_port *port = t7xx_dl->port;
+ u8 *snapshot_mem;
+
+ if (t7xx_dl->status != T7XX_DEVLINK_IDLE)
+ return -EBUSY;
+
+ if (!strncmp(ops->name, "mr_dump", strlen("mr_dump"))) {
+ snapshot_mem = vmalloc(region->info->size);
+ memcpy(snapshot_mem, region->buf, region->info->size);
+ *data = snapshot_mem;
+ } else if (!strncmp(ops->name, "lk_dump", strlen("lk_dump"))) {
+ int ret;
+
+ ret = t7xx_devlink_fb_dump_log(port);
+ if (ret)
+ return ret;
+
+ *data = region->buf;
+ }
+
+ return 0;
+}
+
+static_assert(ARRAY_SIZE(t7xx_devlink_region_infos) ==
+ ARRAY_SIZE(((struct t7xx_devlink *)NULL)->regions));
+
+/* To create regions for dump files */
+static int t7xx_devlink_create_regions(struct t7xx_devlink *dl)
+{
+ int ret, i;
+
+ for (i = 0; i < ARRAY_SIZE(t7xx_devlink_region_infos); i++) {
+ dl->regions[i].info = &t7xx_devlink_region_infos[i];
+ dl->regions[i].ops.name = dl->regions[i].info->name;
+ dl->regions[i].ops.snapshot = t7xx_devlink_region_snapshot;
+ dl->regions[i].ops.destructor = vfree;
+ dl->regions[i].dlreg = devlink_region_create(dl->ctx, &dl->regions[i].ops,
+ T7XX_MAX_SNAPSHOTS,
+ t7xx_devlink_region_infos[i].size);
+ if (IS_ERR(dl->regions[i].dlreg)) {
+ ret = PTR_ERR(dl->regions[i].dlreg);
+ dev_err(dl->port->dev, "create devlink region failed, err %d\n", ret);
+ while (i >= 0)
+ devlink_region_destroy(dl->regions[i--].dlreg);
+
+ return ret;
+ }
+
+ dl->regions[i].ops.priv = &dl->regions[i];
+ }
+
+ return 0;
+}
+
int t7xx_devlink_register(struct t7xx_pci_dev *t7xx_dev)
{
union devlink_param_value value;
@@ -422,6 +629,14 @@ void t7xx_devlink_unregister(struct t7xx_pci_dev *t7xx_dev)
devlink_free(dl_ctx);
}
+static void t7xx_devlink_work(struct work_struct *work)
+{
+ struct t7xx_devlink *dl;
+
+ dl = container_of(work, struct t7xx_devlink, ws);
+ t7xx_devlink_fb_get_core(dl->port);
+}
+
/**
* t7xx_devlink_init - Initialize devlink to t7xx driver
* @port: Pointer to port structure
@@ -431,28 +646,56 @@ void t7xx_devlink_unregister(struct t7xx_pci_dev *t7xx_dev)
static int t7xx_devlink_init(struct t7xx_port *port)
{
struct t7xx_devlink *dl = port->t7xx_dev->dl;
+ struct workqueue_struct *dl_wq;
+ int rc;
+
+ dl_wq = create_workqueue("t7xx_devlink");
+ if (!dl_wq) {
+ dev_err(port->dev, "create_workqueue failed\n");
+ return -ENODATA;
+ }
+ INIT_WORK(&dl->ws, t7xx_devlink_work);
port->rx_length_th = T7XX_MAX_QUEUE_LENGTH;
dl->mode = T7XX_NORMAL_MODE;
dl->status = T7XX_DEVLINK_IDLE;
+ dl->wq = dl_wq;
dl->port = port;
+ rc = t7xx_devlink_create_regions(dl);
+ if (rc) {
+ destroy_workqueue(dl->wq);
+ dev_err(port->dev, "devlink region creation failed, rc %d\n", rc);
+ return -ENOMEM;
+ }
+
return 0;
}
static void t7xx_devlink_uninit(struct t7xx_port *port)
{
struct t7xx_devlink *dl = port->t7xx_dev->dl;
+ int i;
+
+ vfree(dl->regions[T7XX_MRDUMP_INDEX].buf);
dl->mode = T7XX_NORMAL_MODE;
+ destroy_workqueue(dl->wq);
+
+ for (i = 0; i < ARRAY_SIZE(t7xx_devlink_region_infos); ++i)
+ devlink_region_destroy(dl->regions[i].dlreg);
skb_queue_purge(&port->rx_skb_list);
}
static int t7xx_devlink_enable_chl(struct t7xx_port *port)
{
+ struct t7xx_devlink *dl = port->t7xx_dev->dl;
+
t7xx_port_enable_chl(port);
+ if (dl->mode == T7XX_FB_DUMP_MODE)
+ queue_work(dl->wq, &dl->ws);
return 0;
}
diff --git a/drivers/net/wwan/t7xx/t7xx_port_devlink.h b/drivers/net/wwan/t7xx/t7xx_port_devlink.h
index 92f0993e7205..e01845b4f2aa 100644
--- a/drivers/net/wwan/t7xx/t7xx_port_devlink.h
+++ b/drivers/net/wwan/t7xx/t7xx_port_devlink.h
@@ -12,27 +12,66 @@
#define T7XX_MAX_QUEUE_LENGTH 32
#define T7XX_FB_COMMAND_SIZE 64
#define T7XX_FB_RESPONSE_SIZE 512
+#define T7XX_FB_MCMD_SIZE 64
+#define T7XX_FB_MDATA_SIZE 1024
#define T7XX_FB_RESP_COUNT 30
+#define T7XX_FB_EVENT_SIZE 50
+
+#define T7XX_MAX_SNAPSHOTS 1
+#define T7XX_MRDUMP_SIZE (160 * 1024 * 1024)
+#define T7XX_LKDUMP_SIZE (256 * 1024)
+#define T7XX_TOTAL_REGIONS 2
+
#define T7XX_FLASH_STATUS 0
+#define T7XX_MRDUMP_STATUS 1
+#define T7XX_LKDUMP_STATUS 2
#define T7XX_GET_INFO 3
-
#define T7XX_DEVLINK_IDLE 0
+
#define T7XX_NORMAL_MODE 0
#define T7XX_FB_DL_MODE 1
+#define T7XX_FB_DUMP_MODE 2
+#define T7XX_FB_CMD_RTS "_RTS"
+#define T7XX_FB_CMD_CTS "_CTS"
+#define T7XX_FB_CMD_FIN "_FIN"
+#define T7XX_FB_CMD_OEM_MRDUMP "oem mrdump"
+#define T7XX_FB_CMD_OEM_LKDUMP "oem dump_pllk_log"
#define T7XX_FB_CMD_DOWNLOAD "download"
#define T7XX_FB_CMD_FLASH "flash"
#define T7XX_FB_CMD_REBOOT "reboot"
+#define T7XX_FB_RESP_MRDUMP_DONE "MRDUMP08_DONE"
#define T7XX_FB_RESP_OKAY "OKAY"
#define T7XX_FB_RESP_FAIL "FAIL"
#define T7XX_FB_RESP_DATA "DATA"
#define T7XX_FB_RESP_INFO "INFO"
#define T7XX_FB_CMD_GET_VER "get_version"
+/* Internal region indexes */
+enum t7xx_regions {
+ T7XX_MRDUMP_INDEX,
+ T7XX_LKDUMP_INDEX,
+};
+
+struct t7xx_devlink_region_info {
+ const char *name;
+ size_t size;
+};
+
+struct t7xx_devlink_region {
+ struct t7xx_devlink_region_info *info;
+ struct devlink_region_ops ops;
+ struct devlink_region *dlreg;
+ void *buf;
+};
+
struct t7xx_devlink {
+ struct t7xx_devlink_region regions[T7XX_TOTAL_REGIONS];
struct t7xx_pci_dev *t7xx_dev;
+ struct workqueue_struct *wq;
struct t7xx_port *port;
+ struct work_struct ws;
struct devlink *ctx;
unsigned long status;
u8 mode;
diff --git a/drivers/net/wwan/t7xx/t7xx_state_monitor.c b/drivers/net/wwan/t7xx/t7xx_state_monitor.c
index a6147f2324a6..500dadaddb30 100644
--- a/drivers/net/wwan/t7xx/t7xx_state_monitor.c
+++ b/drivers/net/wwan/t7xx/t7xx_state_monitor.c
@@ -244,6 +244,8 @@ static void t7xx_lk_stage_event_handling(struct t7xx_fsm_ctl *ctl, unsigned int
if (lk_event == LK_EVENT_CREATE_POST_DL_PORT)
md->t7xx_dev->dl->mode = T7XX_FB_DL_MODE;
+ else
+ md->t7xx_dev->dl->mode = T7XX_FB_DUMP_MODE;
port->port_conf->ops->enable_chl(port);
t7xx_cldma_start(md_ctrl);
--
2.34.1
Powered by blists - more mailing lists