Prechádzať zdrojové kódy

mm-drivers: hw_fence: add support to read hw fence ctl events

Add support to read hw fence ctl events through debugfs node from the
carved out memory region shared with Fence Controller.

Change-Id: I508695efcb8c7aa8fab9db2086af1ec1ff0ddd84
Signed-off-by: Grace An <[email protected]>
Grace An 2 rokov pred
rodič
commit
cda6ac87d1

+ 26 - 0
hw_fence/include/hw_fence_drv_priv.h

@@ -72,6 +72,12 @@
  */
 #define HW_FENCE_PAYLOAD_REV(major, minor) (major << 8 | (minor & 0xFF))
 
+/**
+ * HW_FENCE_EVENT_MAX_DATA:
+ * Maximum data that can be added to the debug event
+ */
+#define HW_FENCE_EVENT_MAX_DATA 12
+
 enum hw_fence_lookup_ops {
 	HW_FENCE_LOOKUP_OP_CREATE = 0x1,
 	HW_FENCE_LOOKUP_OP_DESTROY,
@@ -265,6 +271,8 @@ struct hw_fence_client_queue_desc {
  * @clients_num: number of supported hw fence clients (configured based on device-tree)
  * @hw_fences_tbl: pointer to the hw-fences table
  * @hw_fences_tbl_cnt: number of elements in the hw-fence table
+ * @events: start address of hw fence debug events
+ * @total_events: total number of hw fence debug events supported
  * @client_lock_tbl: pointer to the per-client locks table
  * @client_lock_tbl_cnt: number of elements in the locks table
  * @hw_fences_mem_desc: memory descriptor for the hw-fence table
@@ -320,6 +328,10 @@ struct hw_fence_driver_data {
 	struct msm_hw_fence *hw_fences_tbl;
 	u32 hw_fences_tbl_cnt;
 
+	/* events */
+	struct msm_hw_fence_event *events;
+	u32 total_events;
+
 	/* Table with a Per-Client Lock */
 	u64 *client_lock_tbl;
 	u32 client_lock_tbl_cnt;
@@ -408,6 +420,20 @@ struct msm_hw_fence_queue_payload {
 	u32 reserve;
 };
 
+/**
+ * struct msm_hw_fence_event - hardware fence ctl debug event
+ * time: qtime when the event is logged
+ * cpu: cpu id where the event is logged
+ * data_cnt: count of valid data available in the data field
+ * data: debug data logged by the event
+ */
+struct msm_hw_fence_event {
+	u64 time;
+	u32 cpu;
+	u32 data_cnt;
+	u32 data[HW_FENCE_EVENT_MAX_DATA];
+};
+
 /**
  * struct msm_hw_fence - structure holding each hw fence data.
  * @valid: field updated when a hw-fence is reserved. True if hw-fence is in use

+ 3 - 1
hw_fence/include/hw_fence_drv_utils.h

@@ -30,12 +30,14 @@
  * HW_FENCE_MEM_RESERVE_LOCKS_REGION: Reserve memory for the per-client locks memory region.
  * HW_FENCE_MEM_RESERVE_TABLE: Reserve memory for the hw-fences global table.
  * HW_FENCE_MEM_RESERVE_CLIENT_QUEUE: Reserve memory per-client for the rx/tx queues.
+ * HW_FENCE_MEM_RESERVE_EVENTS_BUFF: Reserve memory for the debug events
  */
 enum hw_fence_mem_reserve {
 	HW_FENCE_MEM_RESERVE_CTRL_QUEUE,
 	HW_FENCE_MEM_RESERVE_LOCKS_REGION,
 	HW_FENCE_MEM_RESERVE_TABLE,
-	HW_FENCE_MEM_RESERVE_CLIENT_QUEUE
+	HW_FENCE_MEM_RESERVE_CLIENT_QUEUE,
+	HW_FENCE_MEM_RESERVE_EVENTS_BUFF
 };
 
 /**

+ 135 - 0
hw_fence/src/hw_fence_drv_debug.c

@@ -14,6 +14,11 @@
 
 #define HW_FENCE_DEBUG_MAX_LOOPS 200
 
+/* event dump data includes one "32-bit" element + "|" separator */
+#define HW_FENCE_MAX_DATA_PER_EVENT_DUMP (HW_FENCE_EVENT_MAX_DATA * 9)
+
+#define HFENCE_EVT_MSG "[%d][cpu:%d][%lu] data[%d]:%s\n"
+
 u32 msm_hw_fence_debug_level = HW_FENCE_PRINTK;
 
 /**
@@ -540,6 +545,129 @@ static int dump_full_table(struct hw_fence_driver_data *drv_data, char *buf, u32
 	return len;
 }
 
+static inline int _dump_event(struct msm_hw_fence_event *event, char *buf, int len, int max_size,
+	u32 index)
+{
+	char data[HW_FENCE_MAX_DATA_PER_EVENT_DUMP];
+	u32 data_cnt;
+	int i, tmp_len = 0, ret = 0;
+
+	if (!event->time)
+		return 0;
+
+	memset(&data, 0, sizeof(data));
+	if (event->data_cnt > HW_FENCE_EVENT_MAX_DATA) {
+		HWFNC_ERR("event[%d] has invalid data_cnt:%lu greater than max_data_cnt:%lu\n",
+			index, event->data_cnt, HW_FENCE_EVENT_MAX_DATA);
+		data_cnt = HW_FENCE_EVENT_MAX_DATA;
+	} else {
+		data_cnt = event->data_cnt;
+	}
+
+	for (i = 0; i < data_cnt; i++)
+		tmp_len += scnprintf(data + tmp_len, HW_FENCE_MAX_DATA_PER_EVENT_DUMP - tmp_len,
+			"%lx|", event->data[i]);
+
+	ret = scnprintf(buf + len, max_size - len, HFENCE_EVT_MSG, index, event->cpu, event->time,
+		event->data_cnt, data);
+
+	HWFNC_DBG_INFO(HFENCE_EVT_MSG, index, event->cpu, event->time, event->data_cnt, data);
+
+	return ret;
+}
+
+/**
+ * hw_fence_dbg_dump_events_rd() - debugfs read to dump the fctl events.
+ * @file: file handler.
+ * @user_buf: user buffer content for debugfs.
+ * @user_buf_size: size of the user buffer.
+ * @ppos: position offset of the user buffer.
+ */
+static ssize_t hw_fence_dbg_dump_events_rd(struct file *file, char __user *user_buf,
+	size_t user_buf_size, loff_t *ppos)
+{
+	struct hw_fence_driver_data *drv_data;
+	u32 entry_size = sizeof(struct msm_hw_fence_event), max_size = SZ_4K;
+	char *buf = NULL;
+	int len = 0;
+	static u64 start_time;
+	static int index, start_index;
+	static bool wraparound;
+
+	if (!file || !file->private_data) {
+		HWFNC_ERR("unexpected data %d\n", file);
+		return -EINVAL;
+	}
+	drv_data = file->private_data;
+
+	if (!drv_data->events) {
+		HWFNC_ERR("events not supported\n");
+		return -EINVAL;
+	}
+
+	if (wraparound && index >= start_index) {
+		HWFNC_DBG_H("no more data index:%d total_events:%d\n", index,
+			drv_data->total_events);
+		start_time = 0;
+		index = 0;
+		wraparound = false;
+		return 0;
+	}
+
+	if (user_buf_size < entry_size) {
+		HWFNC_ERR("Not enough buff size:%d to dump entries:%d\n", user_buf_size,
+			entry_size);
+		return -EINVAL;
+	}
+
+	buf = kzalloc(max_size, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	/* find index of earliest event */
+	if (!start_time) {
+		mb(); /* make sure data is ready before read */
+		for (index = 0; index < drv_data->total_events; index++) {
+			u64 time = drv_data->events[index].time;
+
+			if (time && (!start_time || time < start_time)) {
+				start_time = time;
+				start_index = index;
+			}
+		}
+		index = start_index;
+		HWFNC_DBG_H("events:0x%pK start_index:%d start_time:%llu total_events:%d\n",
+			drv_data->events, start_index, start_time, drv_data->total_events);
+	}
+
+	HWFNC_DBG_H("++ dump_events index:%d qtime:%llu\n", index, hw_fence_get_qtime(drv_data));
+	while ((!wraparound || index < start_index) && len < (max_size - entry_size)) {
+		len += _dump_event(&drv_data->events[index], buf, len, max_size, index);
+		index++;
+		if (index >= drv_data->total_events) {
+			index = 0;
+			wraparound = true;
+		}
+	}
+	HWFNC_DBG_H("-- dump_events: index:%d qtime:%llu\n", index, hw_fence_get_qtime(drv_data));
+
+	if (len <= 0 || len > user_buf_size) {
+		HWFNC_ERR("len:%d invalid buff size:%d\n", len, user_buf_size);
+		len = 0;
+		goto exit;
+	}
+
+	if (copy_to_user(user_buf, buf, len)) {
+		HWFNC_ERR("failed to copy to user!\n");
+		len = -EFAULT;
+		goto exit;
+	}
+	*ppos += len;
+exit:
+	kfree(buf);
+	return len;
+}
+
 /**
  * hw_fence_dbg_dump_queues_wr() - debugfs wr to dump the hw-fences queues.
  * @file: file handler.
@@ -955,6 +1083,11 @@ static const struct file_operations hw_fence_dump_queues_fops = {
 	.write = hw_fence_dbg_dump_queues_wr,
 };
 
+static const struct file_operations hw_fence_dump_events_fops = {
+	.open = simple_open,
+	.read = hw_fence_dbg_dump_events_rd,
+};
+
 static const struct file_operations hw_fence_create_join_fence_fops = {
 	.open = simple_open,
 	.write = hw_fence_dbg_create_join_fence,
@@ -1004,6 +1137,8 @@ int hw_fence_debug_debugfs_register(struct hw_fence_driver_data *drv_data)
 	debugfs_create_file("hw_sync", 0600, debugfs_root, NULL, &hw_sync_debugfs_fops);
 	debugfs_create_u64("hw_fence_lock_wake_cnt", 0600, debugfs_root,
 		&drv_data->debugfs_data.lock_wake_cnt);
+	debugfs_create_file("hw_fence_dump_events", 0600, debugfs_root, drv_data,
+		&hw_fence_dump_events_fops);
 
 	return 0;
 }

+ 26 - 0
hw_fence/src/hw_fence_drv_priv.c

@@ -502,6 +502,27 @@ static int init_hw_fences_table(struct hw_fence_driver_data *drv_data)
 	return 0;
 }
 
+static int init_hw_fences_events(struct hw_fence_driver_data *drv_data)
+{
+	phys_addr_t phys;
+	void *ptr;
+	u32 size;
+	int ret;
+
+	ret = hw_fence_utils_reserve_mem(drv_data, HW_FENCE_MEM_RESERVE_EVENTS_BUFF, &phys, &ptr,
+		&size, 0);
+	if (ret) {
+		HWFNC_DBG_INFO("Failed to reserve events buffer %d\n", ret);
+		return -ENOMEM;
+	}
+	drv_data->events = (struct msm_hw_fence_event *)ptr;
+	drv_data->total_events = size / sizeof(struct msm_hw_fence_event);
+	HWFNC_DBG_INIT("events:0x%pK total_events:%u event_sz:%u total_size:%u\n", drv_data->events,
+		drv_data->total_events, sizeof(struct msm_hw_fence_event), size);
+
+	return 0;
+}
+
 static int init_ctrl_queue(struct hw_fence_driver_data *drv_data)
 {
 	struct msm_hw_fence_mem_addr *mem_descriptor;
@@ -553,6 +574,11 @@ int hw_fence_init(struct hw_fence_driver_data *drv_data)
 	if (ret)
 		goto exit;
 
+	/* Initialize event log */
+	ret = init_hw_fences_events(drv_data);
+	if (ret)
+		HWFNC_DBG_INFO("Unable to init events\n");
+
 	/* Map ipcc registers */
 	ret = hw_fence_utils_map_ipcc(drv_data);
 	if (ret) {

+ 26 - 0
hw_fence/src/hw_fence_drv_utils.c

@@ -56,6 +56,12 @@
  */
 #define HW_FENCE_LOOPBACK_CLIENTS_MASK 0x7fff
 
+/**
+ * HW_FENCE_MAX_EVENTS:
+ * Maximum number of HW Fence debug events
+ */
+#define HW_FENCE_MAX_EVENTS 1000
+
 /**
  * struct hw_fence_client_types - Table describing all supported client types, used to parse
  *                                device-tree properties related to client queue size.
@@ -472,6 +478,8 @@ char *_get_mem_reserve_type(enum hw_fence_mem_reserve type)
 		return "HW_FENCE_MEM_RESERVE_TABLE";
 	case HW_FENCE_MEM_RESERVE_CLIENT_QUEUE:
 		return "HW_FENCE_MEM_RESERVE_CLIENT_QUEUE";
+	case HW_FENCE_MEM_RESERVE_EVENTS_BUFF:
+		return "HW_FENCE_MEM_RESERVE_EVENTS_BUFF";
 	}
 
 	return "Unknown";
@@ -483,6 +491,8 @@ int hw_fence_utils_reserve_mem(struct hw_fence_driver_data *drv_data,
 {
 	int ret = 0;
 	u32 start_offset = 0;
+	u32 remaining_size_bytes;
+	u32 total_events;
 
 	switch (type) {
 	case HW_FENCE_MEM_RESERVE_CTRL_QUEUE:
@@ -512,6 +522,22 @@ int hw_fence_utils_reserve_mem(struct hw_fence_driver_data *drv_data,
 		start_offset = drv_data->hw_fence_client_queue_size[client_id].start_offset;
 		*size = drv_data->hw_fence_client_queue_size[client_id].type->mem_size;
 		break;
+	case HW_FENCE_MEM_RESERVE_EVENTS_BUFF:
+		start_offset = drv_data->used_mem_size;
+		remaining_size_bytes = drv_data->size - start_offset;
+		if (start_offset >= drv_data->size ||
+				remaining_size_bytes < sizeof(struct msm_hw_fence_event)) {
+			HWFNC_DBG_INFO("no space for events total_sz:%lu offset:%lu evt_sz:%lu\n",
+				drv_data->size, start_offset, sizeof(struct msm_hw_fence_event));
+			ret = -ENOMEM;
+			goto exit;
+		}
+
+		total_events = remaining_size_bytes / sizeof(struct msm_hw_fence_event);
+		if (total_events > HW_FENCE_MAX_EVENTS)
+			total_events = HW_FENCE_MAX_EVENTS;
+		*size = total_events * sizeof(struct msm_hw_fence_event);
+		break;
 	default:
 		HWFNC_ERR("Invalid mem reserve type:%d\n", type);
 		ret = -EINVAL;