Browse Source

video: driver: enable smmu_fault_handler support

Added smmu_fault_handler support and printing all buffers,
allocations, mappings list details as part of smmu fault
handling.

Change-Id: Idb5d28357f9fb885adf7ae16f328b4963c56aa8c
Signed-off-by: Govindaraj Rajagopal <[email protected]>
Govindaraj Rajagopal 4 năm trước cách đây
mục cha
commit
4055a23255

+ 1 - 0
driver/platform/waipio/src/msm_vidc_waipio.c

@@ -86,6 +86,7 @@ static struct msm_platform_core_capability core_data_waipio[] = {
 	{DECODE_BATCH, 1},
 	{DECODE_BATCH_TIMEOUT, 200},
 	{AV_SYNC_WINDOW_SIZE, 40},
+	{NON_FATAL_FAULTS, 1},
 };
 
 static struct msm_platform_inst_capability instance_data_waipio[] = {

+ 1 - 0
driver/vidc/inc/msm_vidc_core.h

@@ -84,6 +84,7 @@ struct msm_vidc_core {
 	struct msm_vidc_mem_addr               iface_q_table;
 	struct msm_vidc_iface_q_info           iface_queues[VIDC_IFACEQ_NUMQ];
 	struct work_struct                     device_work;
+	struct work_struct                     smmu_fault_work;
 	struct workqueue_struct               *device_workq;
 	struct delayed_work                    pm_work;
 	struct workqueue_struct               *pm_workq;

+ 12 - 0
driver/vidc/inc/msm_vidc_debug.h

@@ -14,6 +14,10 @@
 #define VIDC_DBG_LABEL "msm_vidc"
 #endif
 
+/* Allow only 6 prints/sec */
+#define VIDC_DBG_SESSION_RATELIMIT_INTERVAL (1 * HZ)
+#define VIDC_DBG_SESSION_RATELIMIT_BURST 6
+
 #define VIDC_DBG_TAG VIDC_DBG_LABEL ": %6s: %08x: %5s: "
 #define FW_DBG_TAG VIDC_DBG_LABEL ": %6s: "
 #define DEFAULT_SID ((u32)-1)
@@ -106,6 +110,13 @@ enum vidc_msg_prio {
 			##__VA_ARGS__); \
 	} while (0)
 
+#define dprintk_ratelimit(__level, __fmt, ...) \
+	do { \
+		if (msm_vidc_check_ratelimit()) { \
+			dprintk(__level, DEFAULT_SID, __fmt, ##__VA_ARGS__); \
+		} \
+	} while (0)
+
 #define MSM_VIDC_ERROR(value)					\
 	do {	if (value)					\
 			d_vpr_e("BugOn");		\
@@ -129,5 +140,6 @@ struct dentry *msm_vidc_debugfs_init_inst(void *inst,
 void msm_vidc_debugfs_deinit_inst(void *inst);
 void msm_vidc_debugfs_update(void *inst,
 		enum msm_vidc_debugfs_event e);
+int msm_vidc_check_ratelimit(void);
 
 #endif

+ 2 - 0
driver/vidc/inc/msm_vidc_driver.h

@@ -229,6 +229,8 @@ int msm_vidc_change_core_state(struct msm_vidc_core *core,
 int msm_vidc_core_init(struct msm_vidc_core *core);
 int msm_vidc_core_deinit(struct msm_vidc_core *core, bool force);
 int msm_vidc_core_timeout(struct msm_vidc_core *core);
+int msm_vidc_print_inst_info(struct msm_vidc_inst *inst);
+void msm_vidc_smmu_fault_work_handler(struct work_struct *work);
 int msm_vidc_smmu_fault_handler(struct iommu_domain *domain,
 		struct device *dev, unsigned long iova, int flags, void *data);
 int msm_vidc_trigger_ssr(struct msm_vidc_core *core,

+ 1 - 0
driver/vidc/inc/msm_vidc_internal.h

@@ -229,6 +229,7 @@ enum msm_vidc_core_capability_type {
 	DECODE_BATCH_TIMEOUT,
 	AV_SYNC_WINDOW_SIZE,
 	CLK_FREQ_THRESHOLD,
+	NON_FATAL_FAULTS,
 	CORE_CAP_MAX,
 };
 

+ 7 - 1
driver/vidc/src/msm_vidc_debug.c

@@ -559,4 +559,10 @@ void msm_vidc_debugfs_update(void *instance,
 	}
 }
 
-
+int msm_vidc_check_ratelimit(void)
+{
+	static DEFINE_RATELIMIT_STATE(_rs,
+				VIDC_DBG_SESSION_RATELIMIT_INTERVAL,
+				VIDC_DBG_SESSION_RATELIMIT_BURST);
+	return __ratelimit(&_rs);
+}

+ 137 - 1
driver/vidc/src/msm_vidc_driver.c

@@ -29,6 +29,11 @@
 	}                           \
 }
 
+struct msm_vidc_buf_details {
+	enum msm_vidc_buffer_type type;
+	char *name;
+};
+
 void print_vidc_buffer(u32 tag, const char *str, struct msm_vidc_inst *inst,
 		struct msm_vidc_buffer *vbuf)
 {
@@ -2625,10 +2630,141 @@ int msm_vidc_core_timeout(struct msm_vidc_core *core)
 	return msm_vidc_core_deinit(core, true);
 }
 
+int msm_vidc_print_inst_info(struct msm_vidc_inst *inst)
+{
+	struct msm_vidc_buffers *buffers;
+	struct msm_vidc_buffer *buf;
+	enum msm_vidc_port_type port;
+	bool is_secure, is_decode;
+	u32 bit_depth, bit_rate, frame_rate, width, height;
+	struct dma_buf *dbuf;
+	int i = 0;
+
+	struct msm_vidc_buf_details buffer_details[] = {
+		{MSM_VIDC_BUF_INPUT,             "INPUT"      },
+		{MSM_VIDC_BUF_OUTPUT,            "OUTPUT"     },
+		{MSM_VIDC_BUF_INPUT_META,        "IN_META"    },
+		{MSM_VIDC_BUF_OUTPUT_META,       "OUT_META"   },
+		{MSM_VIDC_BUF_BIN,               "BIN"        },
+		{MSM_VIDC_BUF_ARP,               "ARP"        },
+		{MSM_VIDC_BUF_COMV,              "COMV"       },
+		{MSM_VIDC_BUF_NON_COMV,          "NON_COMV"   },
+		{MSM_VIDC_BUF_LINE,              "LINE"       },
+		{MSM_VIDC_BUF_PERSIST,           "PERSIST"    },
+		{MSM_VIDC_BUF_VPSS,              "VPSS"       },
+	};
+
+	if (!inst || !inst->capabilities) {
+		i_vpr_e(inst, "%s: invalid params\n", __func__);
+		return -EINVAL;
+	}
+
+	is_secure = !!(inst->flags & VIDC_SECURE);
+	is_decode = inst->domain == MSM_VIDC_DECODER;
+	port = is_decode ? INPUT_PORT : OUTPUT_PORT;
+	width = inst->fmts[port].fmt.pix_mp.width;
+	height = inst->fmts[port].fmt.pix_mp.height;
+	bit_depth = inst->capabilities->cap[BIT_DEPTH].value & 0xFFFF;
+	bit_rate = inst->capabilities->cap[BIT_RATE].value;
+	frame_rate = inst->capabilities->cap[FRAME_RATE].value >> 16;
+
+	i_vpr_e(inst, "%s %s session, HxW: %d x %d, fps: %d, bitrate: %d, bit-depth: %d\n",
+		is_secure ? "Secure" : "Non-Secure",
+		is_decode ? "Decode" : "Encode",
+		height, width,
+		frame_rate, bit_rate, bit_depth);
+
+	/* Print buffer details */
+	for (i = 0; i < ARRAY_SIZE(buffer_details); i++) {
+		buffers = msm_vidc_get_buffers(inst, buffer_details[i].type, __func__);
+		if (!buffers)
+			continue;
+
+		i_vpr_e(inst, "count: type: %8s, min: %2d, extra: %2d, actual: %2d\n",
+			buffer_details[i].name, buffers->min_count,
+			buffers->extra_count, buffers->actual_count);
+
+		list_for_each_entry(buf, &buffers->list, list) {
+			if (!buf->valid || !buf->dmabuf)
+				continue;
+			dbuf = (struct dma_buf *)buf->dmabuf;
+			i_vpr_e(inst,
+				"buf: type: %8s, index: %2d, fd: %4d, size: %9u, off: %8u, filled: %9u, iova: %8x, inode: %9ld, flags: %8x, ts: %16lld, attr: %8x\n",
+				buffer_details[i].name, buf->index, buf->fd, buf->buffer_size,
+				buf->data_offset, buf->data_size, buf->device_addr,
+				file_inode(dbuf->file)->i_ino,
+				buf->flags, buf->timestamp, buf->attr);
+		}
+	}
+
+	return 0;
+}
+
+void msm_vidc_smmu_fault_work_handler(struct work_struct *work)
+{
+	struct msm_vidc_core *core;
+	struct msm_vidc_inst *inst = NULL;
+	struct msm_vidc_inst *instances[MAX_SUPPORTED_INSTANCES];
+	s32 num_instances = 0;
+
+	core = container_of(work, struct msm_vidc_core, smmu_fault_work);
+	if (!core) {
+		d_vpr_e("%s: invalid params\n", __func__);
+		return;
+	}
+
+	core_lock(core, __func__);
+	list_for_each_entry(inst, &core->instances, list)
+		instances[num_instances++] = inst;
+	core_unlock(core, __func__);
+
+	while (num_instances--) {
+		inst = instances[num_instances];
+		inst = get_inst_ref(core, inst);
+		if (!inst)
+			continue;
+		inst_lock(inst, __func__);
+		msm_vidc_print_inst_info(inst);
+		inst_unlock(inst, __func__);
+		put_inst(inst);
+	}
+}
+
 int msm_vidc_smmu_fault_handler(struct iommu_domain *domain,
 		struct device *dev, unsigned long iova, int flags, void *data)
 {
-	return -EINVAL;
+	struct msm_vidc_core *core = data;
+
+	if (!domain || !core || !core->capabilities) {
+		d_vpr_e("%s: invalid params %pK %pK\n",
+			__func__, domain, core);
+		return -EINVAL;
+	}
+
+	if (core->smmu_fault_handled) {
+		if (core->capabilities[NON_FATAL_FAULTS].value) {
+			dprintk_ratelimit(VIDC_ERR,
+					"%s: non-fatal pagefault address: %lx\n",
+					__func__, iova);
+			return 0;
+		}
+	}
+
+	d_vpr_e("%s: faulting address: %lx\n", __func__, iova);
+
+	core->smmu_fault_handled = true;
+	/**
+	 * Fault handler shouldn't be blocked for longtime. So offload work
+	 * to device_workq to print buffer and memory consumption details.
+	 */
+	queue_work(core->device_workq, &core->smmu_fault_work);
+	/*
+	 * Return -ENOSYS to elicit the default behaviour of smmu driver.
+	 * If we return -ENOSYS, then smmu driver assumes page fault handler
+	 * is not installed and prints a list of useful debug information like
+	 * FAR, SID etc. This information is not printed if we return 0.
+	 */
+	return -ENOSYS;
 }
 
 int msm_vidc_trigger_ssr(struct msm_vidc_core *core,

+ 1 - 0
driver/vidc/src/msm_vidc_probe.c

@@ -221,6 +221,7 @@ static int msm_vidc_initialize_core(struct msm_vidc_core *core)
 	INIT_LIST_HEAD(&core->dangling_instances);
 
 	INIT_WORK(&core->device_work, venus_hfi_work_handler);
+	INIT_WORK(&core->smmu_fault_work, msm_vidc_smmu_fault_work_handler);
 	INIT_DELAYED_WORK(&core->pm_work, venus_hfi_pm_work_handler);
 	INIT_DELAYED_WORK(&core->fw_unload_work, msm_vidc_fw_unload_handler);
 	INIT_DELAYED_WORK(&core->batch_work, msm_vidc_batch_handler);