]> git.proxmox.com Git - mirror_ubuntu-kernels.git/commitdiff
net: wwan: iosm: device trace collection using relayfs
authorM Chetan Kumar <m.chetan.kumar@linux.intel.com>
Sat, 20 Nov 2021 16:21:55 +0000 (21:51 +0530)
committerDavid S. Miller <davem@davemloft.net>
Mon, 22 Nov 2021 13:23:08 +0000 (13:23 +0000)
This patch brings in support for device trace collection.
It implements relayfs interface for pushing device trace
from kernel space to user space.

Driver gets the debugfs base directory associated to WWAN
Device and creates trace_control and trace debugfs for
device tracing. Both trace_control & trace debugfs are
created under /sys/kernel/debug/wwan/wwan0/.

In order to collect device trace on trace0 interface, user
need to write 1 to trace_ctl interface.

Signed-off-by: M Chetan Kumar <m.chetan.kumar@linux.intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/wwan/iosm/Makefile
drivers/net/wwan/iosm/iosm_ipc_imem.c
drivers/net/wwan/iosm/iosm_ipc_imem.h
drivers/net/wwan/iosm/iosm_ipc_imem_ops.c
drivers/net/wwan/iosm/iosm_ipc_imem_ops.h
drivers/net/wwan/iosm/iosm_ipc_port.c
drivers/net/wwan/iosm/iosm_ipc_trace.c [new file with mode: 0644]
drivers/net/wwan/iosm/iosm_ipc_trace.h [new file with mode: 0644]

index b838034bb120e5bfcdf3396ed5ea1964957af645..5c2528beca2a88fea42cc65e3f73f96b5619bc33 100644 (file)
@@ -21,6 +21,7 @@ iosm-y = \
        iosm_ipc_mux_codec.o            \
        iosm_ipc_devlink.o              \
        iosm_ipc_flash.o                \
-       iosm_ipc_coredump.o
+       iosm_ipc_coredump.o             \
+       iosm_ipc_trace.o
 
 obj-$(CONFIG_IOSM) := iosm.o
index cff3b43ca4d7d3f9ce813aef5e9148c10f0c1979..1be07114c85dd7daa997ccb4f1b0e95919fec4c1 100644 (file)
@@ -10,6 +10,7 @@
 #include "iosm_ipc_flash.h"
 #include "iosm_ipc_imem.h"
 #include "iosm_ipc_port.h"
+#include "iosm_ipc_trace.h"
 
 /* Check the wwan ips if it is valid with Channel as input. */
 static int ipc_imem_check_wwan_ips(struct ipc_mem_channel *chnl)
@@ -265,9 +266,14 @@ static void ipc_imem_dl_skb_process(struct iosm_imem *ipc_imem,
        switch (pipe->channel->ctype) {
        case IPC_CTYPE_CTRL:
                port_id = pipe->channel->channel_id;
+               ipc_pcie_addr_unmap(ipc_imem->pcie, IPC_CB(skb)->len,
+                                   IPC_CB(skb)->mapping,
+                                   IPC_CB(skb)->direction);
                if (port_id == IPC_MEM_CTRL_CHL_ID_7)
                        ipc_imem_sys_devlink_notify_rx(ipc_imem->ipc_devlink,
                                                       skb);
+               else if (port_id == ipc_imem->trace->chl_id)
+                       ipc_trace_port_rx(ipc_imem->trace, skb);
                else
                        wwan_port_rx(ipc_imem->ipc_port[port_id]->iosm_port,
                                     skb);
@@ -548,6 +554,12 @@ static void ipc_imem_run_state_worker(struct work_struct *instance)
                ctrl_chl_idx++;
        }
 
+       ipc_imem->trace = ipc_imem_trace_channel_init(ipc_imem);
+       if (!ipc_imem->trace) {
+               dev_err(ipc_imem->dev, "trace channel init failed");
+               return;
+       }
+
        ipc_task_queue_send_task(ipc_imem, ipc_imem_send_mdm_rdy_cb, 0, NULL, 0,
                                 false);
 
@@ -1163,6 +1175,7 @@ void ipc_imem_cleanup(struct iosm_imem *ipc_imem)
 
        if (test_and_clear_bit(FULLY_FUNCTIONAL, &ipc_imem->flag)) {
                ipc_mux_deinit(ipc_imem->mux);
+               ipc_trace_deinit(ipc_imem->trace);
                ipc_wwan_deinit(ipc_imem->wwan);
                ipc_port_deinit(ipc_imem->ipc_port);
        }
index 6be6708b4eec86c71309513880d9242501a3d624..cec38009c44a7ac3e5d726ff15b9ca967c4ee2c3 100644 (file)
@@ -304,6 +304,7 @@ enum ipc_phase {
  * @sio:                       IPC SIO data structure pointer
  * @ipc_port:                  IPC PORT data structure pointer
  * @pcie:                      IPC PCIe
+ * @trace:                     IPC trace data structure pointer
  * @dev:                       Pointer to device structure
  * @ipc_requested_state:       Expected IPC state on CP.
  * @channels:                  Channel list with UL/DL pipe pairs.
@@ -349,6 +350,7 @@ struct iosm_imem {
        struct iosm_mux *mux;
        struct iosm_cdev *ipc_port[IPC_MEM_MAX_CHANNELS];
        struct iosm_pcie *pcie;
+       struct iosm_trace *trace;
        struct device *dev;
        enum ipc_mem_device_ipc_state ipc_requested_state;
        struct ipc_mem_channel channels[IPC_MEM_MAX_CHANNELS];
index 825e8e5ffb2aedc8c32f0066e1451ab9b778b0b5..43f1796a898444831c5fa6a8a0ab5050da0ee9d0 100644 (file)
@@ -11,6 +11,7 @@
 #include "iosm_ipc_imem_ops.h"
 #include "iosm_ipc_port.h"
 #include "iosm_ipc_task_queue.h"
+#include "iosm_ipc_trace.h"
 
 /* Open a packet data online channel between the network layer and CP. */
 int ipc_imem_sys_wwan_open(struct iosm_imem *ipc_imem, int if_id)
@@ -107,6 +108,23 @@ void ipc_imem_wwan_channel_init(struct iosm_imem *ipc_imem,
                        "failed to register the ipc_wwan interfaces");
 }
 
+/**
+ * ipc_imem_trace_channel_init - Initializes trace channel.
+ * @ipc_imem:          Pointer to iosm_imem struct.
+ *
+ * Returns: Pointer to trace instance on success else NULL
+ */
+struct iosm_trace *ipc_imem_trace_channel_init(struct iosm_imem *ipc_imem)
+{
+       struct ipc_chnl_cfg chnl_cfg = { 0 };
+
+       ipc_chnl_cfg_get(&chnl_cfg, IPC_MEM_CTRL_CHL_ID_3);
+       ipc_imem_channel_init(ipc_imem, IPC_CTYPE_CTRL, chnl_cfg,
+                             IRQ_MOD_OFF);
+
+       return ipc_trace_init(ipc_imem);
+}
+
 /* Map SKB to DMA for transfer */
 static int ipc_imem_map_skb_to_dma(struct iosm_imem *ipc_imem,
                                   struct sk_buff *skb)
@@ -182,11 +200,14 @@ channel_unavailable:
        return false;
 }
 
-/* Release a sio link to CP. */
-void ipc_imem_sys_cdev_close(struct iosm_cdev *ipc_cdev)
+/**
+ * ipc_imem_sys_port_close - Release a sio link to CP.
+ * @ipc_imem:          Imem instance.
+ * @channel:           Channel instance.
+ */
+void ipc_imem_sys_port_close(struct iosm_imem *ipc_imem,
+                            struct ipc_mem_channel *channel)
 {
-       struct iosm_imem *ipc_imem = ipc_cdev->ipc_imem;
-       struct ipc_mem_channel *channel = ipc_cdev->channel;
        enum ipc_phase curr_phase;
        int status = 0;
        u32 tail = 0;
@@ -643,6 +664,6 @@ int ipc_imem_sys_devlink_read(struct iosm_devlink *devlink, u8 *data,
        memcpy(data, skb->data, skb->len);
 
 devlink_read_fail:
-       ipc_pcie_kfree_skb(devlink->pcie, skb);
+       dev_kfree_skb(skb);
        return rc;
 }
index f0c88ac5643c625e6b8d412b422af651767c052a..e36ee2782629dc316963dd6dd39ffb92b5d91820 100644 (file)
  */
 struct ipc_mem_channel *ipc_imem_sys_port_open(struct iosm_imem *ipc_imem,
                                               int chl_id, int hp_id);
-
-/**
- * ipc_imem_sys_cdev_close - Release a sio link to CP.
- * @ipc_cdev:          iosm sio instance.
- */
-void ipc_imem_sys_cdev_close(struct iosm_cdev *ipc_cdev);
+void ipc_imem_sys_port_close(struct iosm_imem *ipc_imem,
+                            struct ipc_mem_channel *channel);
 
 /**
  * ipc_imem_sys_cdev_write - Route the uplink buffer to CP.
@@ -145,4 +141,5 @@ int ipc_imem_sys_devlink_read(struct iosm_devlink *ipc_devlink, u8 *data,
  */
 int ipc_imem_sys_devlink_write(struct iosm_devlink *ipc_devlink,
                               unsigned char *buf, int count);
+struct iosm_trace *ipc_imem_trace_channel_init(struct iosm_imem *ipc_imem);
 #endif
index beb944847398b2d86cd5d9651c826785941edf26..b6d81c6272771573007f3091371400df0ac02496 100644 (file)
@@ -27,7 +27,7 @@ static void ipc_port_ctrl_stop(struct wwan_port *port)
 {
        struct iosm_cdev *ipc_port = wwan_port_get_drvdata(port);
 
-       ipc_imem_sys_cdev_close(ipc_port);
+       ipc_imem_sys_port_close(ipc_port->ipc_imem, ipc_port->channel);
 }
 
 /* transfer control data to modem */
diff --git a/drivers/net/wwan/iosm/iosm_ipc_trace.c b/drivers/net/wwan/iosm/iosm_ipc_trace.c
new file mode 100644 (file)
index 0000000..c5fa125
--- /dev/null
@@ -0,0 +1,173 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2020-2021 Intel Corporation.
+ */
+
+#include <linux/wwan.h>
+#include "iosm_ipc_trace.h"
+
+/* sub buffer size and number of sub buffer */
+#define IOSM_TRC_SUB_BUFF_SIZE 131072
+#define IOSM_TRC_N_SUB_BUFF 32
+
+#define IOSM_TRC_FILE_PERM 0600
+
+#define IOSM_TRC_DEBUGFS_TRACE "trace"
+#define IOSM_TRC_DEBUGFS_TRACE_CTRL "trace_ctrl"
+
+/**
+ * ipc_trace_port_rx - Receive trace packet from cp and write to relay buffer
+ * @ipc_trace:  Pointer to the ipc trace data-struct
+ * @skb:        Pointer to struct sk_buff
+ */
+void ipc_trace_port_rx(struct iosm_trace *ipc_trace, struct sk_buff *skb)
+{
+       if (ipc_trace->ipc_rchan)
+               relay_write(ipc_trace->ipc_rchan, skb->data, skb->len);
+
+       dev_kfree_skb(skb);
+}
+
+/* Creates relay file in debugfs. */
+static struct dentry *
+ipc_trace_create_buf_file_handler(const char *filename,
+                                 struct dentry *parent,
+                                 umode_t mode,
+                                 struct rchan_buf *buf,
+                                 int *is_global)
+{
+       *is_global = 1;
+       return debugfs_create_file(filename, mode, parent, buf,
+                                  &relay_file_operations);
+}
+
+/* Removes relay file from debugfs. */
+static int ipc_trace_remove_buf_file_handler(struct dentry *dentry)
+{
+       debugfs_remove(dentry);
+       return 0;
+}
+
+static int ipc_trace_subbuf_start_handler(struct rchan_buf *buf, void *subbuf,
+                                         void *prev_subbuf,
+                                         size_t prev_padding)
+{
+       if (relay_buf_full(buf)) {
+               pr_err_ratelimited("Relay_buf full dropping traces");
+               return 0;
+       }
+
+       return 1;
+}
+
+/* Relay interface callbacks */
+static struct rchan_callbacks relay_callbacks = {
+       .subbuf_start = ipc_trace_subbuf_start_handler,
+       .create_buf_file = ipc_trace_create_buf_file_handler,
+       .remove_buf_file = ipc_trace_remove_buf_file_handler,
+};
+
+/* Copy the trace control mode to user buffer */
+static ssize_t ipc_trace_ctrl_file_read(struct file *filp, char __user *buffer,
+                                       size_t count, loff_t *ppos)
+{
+       struct iosm_trace *ipc_trace = filp->private_data;
+       char buf[16];
+       int len;
+
+       mutex_lock(&ipc_trace->trc_mutex);
+       len = snprintf(buf, sizeof(buf), "%d\n", ipc_trace->mode);
+       mutex_unlock(&ipc_trace->trc_mutex);
+
+       return simple_read_from_buffer(buffer, count, ppos, buf, len);
+}
+
+/* Open and close the trace channel depending on user input */
+static ssize_t ipc_trace_ctrl_file_write(struct file *filp,
+                                        const char __user *buffer,
+                                        size_t count, loff_t *ppos)
+{
+       struct iosm_trace *ipc_trace = filp->private_data;
+       unsigned long val;
+       int ret;
+
+       ret = kstrtoul_from_user(buffer, count, 10, &val);
+       if (ret)
+               return ret;
+
+       mutex_lock(&ipc_trace->trc_mutex);
+       if (val == TRACE_ENABLE && ipc_trace->mode != TRACE_ENABLE) {
+               ipc_trace->channel = ipc_imem_sys_port_open(ipc_trace->ipc_imem,
+                                                           ipc_trace->chl_id,
+                                                           IPC_HP_CDEV_OPEN);
+               if (!ipc_trace->channel) {
+                       ret = -EIO;
+                       goto unlock;
+               }
+               ipc_trace->mode = TRACE_ENABLE;
+       } else if (val == TRACE_DISABLE && ipc_trace->mode != TRACE_DISABLE) {
+               ipc_trace->mode = TRACE_DISABLE;
+               /* close trace channel */
+               ipc_imem_sys_port_close(ipc_trace->ipc_imem,
+                                       ipc_trace->channel);
+               relay_flush(ipc_trace->ipc_rchan);
+       }
+       ret = count;
+unlock:
+       mutex_unlock(&ipc_trace->trc_mutex);
+       return ret;
+}
+
+static const struct file_operations ipc_trace_fops = {
+       .open = simple_open,
+       .write = ipc_trace_ctrl_file_write,
+       .read  = ipc_trace_ctrl_file_read,
+};
+
+/**
+ * ipc_trace_init - Create trace interface & debugfs entries
+ * @ipc_imem:   Pointer to iosm_imem structure
+ *
+ * Returns: Pointer to trace instance on success else NULL
+ */
+struct iosm_trace *ipc_trace_init(struct iosm_imem *ipc_imem)
+{
+       struct iosm_trace *ipc_trace = kzalloc(sizeof(*ipc_trace), GFP_KERNEL);
+       struct dentry *debugfs_pdev;
+
+       if (!ipc_trace)
+               return NULL;
+
+       ipc_trace->mode = TRACE_DISABLE;
+       ipc_trace->dev = ipc_imem->dev;
+       ipc_trace->ipc_imem = ipc_imem;
+       ipc_trace->chl_id = IPC_MEM_CTRL_CHL_ID_3;
+
+       mutex_init(&ipc_trace->trc_mutex);
+       debugfs_pdev = wwan_get_debugfs_dir(ipc_imem->dev);
+
+       ipc_trace->ctrl_file = debugfs_create_file(IOSM_TRC_DEBUGFS_TRACE_CTRL,
+                                                  IOSM_TRC_FILE_PERM,
+                                                  debugfs_pdev,
+                                                  ipc_trace, &ipc_trace_fops);
+
+       ipc_trace->ipc_rchan = relay_open(IOSM_TRC_DEBUGFS_TRACE,
+                                         debugfs_pdev,
+                                         IOSM_TRC_SUB_BUFF_SIZE,
+                                         IOSM_TRC_N_SUB_BUFF,
+                                         &relay_callbacks, NULL);
+
+       return ipc_trace;
+}
+
+/**
+ * ipc_trace_deinit - Closing relayfs, removing debugfs entries
+ * @ipc_trace: Pointer to the iosm_trace data struct
+ */
+void ipc_trace_deinit(struct iosm_trace *ipc_trace)
+{
+       debugfs_remove(ipc_trace->ctrl_file);
+       relay_close(ipc_trace->ipc_rchan);
+       mutex_destroy(&ipc_trace->trc_mutex);
+       kfree(ipc_trace);
+}
diff --git a/drivers/net/wwan/iosm/iosm_ipc_trace.h b/drivers/net/wwan/iosm/iosm_ipc_trace.h
new file mode 100644 (file)
index 0000000..5334618
--- /dev/null
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ * Copyright (C) 2020-2021 Intel Corporation.
+ */
+
+#ifndef IOSM_IPC_TRACE_H
+#define IOSM_IPC_TRACE_H
+
+#include <linux/debugfs.h>
+#include <linux/relay.h>
+
+#include "iosm_ipc_chnl_cfg.h"
+#include "iosm_ipc_imem_ops.h"
+
+/**
+ * enum trace_ctrl_mode - State of trace channel
+ * @TRACE_DISABLE:     mode for disable trace
+ * @TRACE_ENABLE:      mode for enable trace
+ */
+enum trace_ctrl_mode {
+       TRACE_DISABLE = 0,
+       TRACE_ENABLE,
+};
+
+/**
+ * struct iosm_trace - Struct for trace interface
+ * @ipc_rchan:         Pointer to relay channel
+ * @ctrl_file:         Pointer to trace control file
+ * @ipc_imem:          Imem instance
+ * @dev:               Pointer to device struct
+ * @channel:           Channel instance
+ * @chl_id:            Channel Indentifier
+ * @trc_mutex:         Mutex used for read and write mode
+ * @mode:              Mode for enable and disable trace
+ */
+
+struct iosm_trace {
+       struct rchan *ipc_rchan;
+       struct dentry *ctrl_file;
+       struct iosm_imem *ipc_imem;
+       struct device *dev;
+       struct ipc_mem_channel *channel;
+       enum ipc_channel_id chl_id;
+       struct mutex trc_mutex; /* Mutex used for read and write mode */
+       enum trace_ctrl_mode mode;
+};
+
+struct iosm_trace *ipc_trace_init(struct iosm_imem *ipc_imem);
+void ipc_trace_deinit(struct iosm_trace *ipc_trace);
+void ipc_trace_port_rx(struct iosm_trace *ipc_trace, struct sk_buff *skb);
+#endif