Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit aff901ae authored by Gaurav Jindal's avatar Gaurav Jindal Committed by Gerrit - the friendly Code Review server
Browse files

msm: camera: req_mgr: LDAR Debug framework implementation



When user space detects an error or does not receive
response for a request, Lets do a reset(LDAR) is triggered.
Before LDAR, user space sends flush command to the
kernel space.
To debug the cause for this situation and to dump
the information, user space sends a dump command to the
kernel space before sending flush.
As a part of this command, it passes the culprit request id
and the buffer into which the information can be dumped.
Kernel space traverses across the drivers and find the culprit hw
and dumps the relevant information in the buffer.
This data is written to a file for offline processing.
This commit implements the framework for traversal
across the RT and NRT devices.

CRs-Fixed: 2503859
Change-Id: I7e24006c20c23bfab163a2ad13b4ac6e2913bb9e
Signed-off-by: default avatarGaurav Jindal <gjindal@codeaurora.org>
parent 643c7c3e
Loading
Loading
Loading
Loading
+65 −0
Original line number Diff line number Diff line
@@ -230,6 +230,34 @@ int cam_context_handle_crm_process_evt(struct cam_context *ctx,
	return rc;
}

int cam_context_handle_crm_dump_req(struct cam_context *ctx,
	struct cam_req_mgr_dump_info *dump)
{
	int rc = 0;

	if (!ctx) {
		CAM_ERR(CAM_CORE, "Invalid Context");
		return -EINVAL;
	}
	if (!ctx->state_machine) {
		CAM_ERR(CAM_CORE, "Context %s ctx_id %d is not ready",
			ctx->dev_name, ctx->ctx_id);
		return -EINVAL;
	}
	mutex_lock(&ctx->ctx_mutex);

	if (ctx->state_machine[ctx->state].crm_ops.dump_req)
		rc = ctx->state_machine[ctx->state].crm_ops.dump_req(ctx,
			dump);
	else
		CAM_ERR(CAM_CORE, "No crm dump req for %s dev %d, state %d",
			ctx->dev_name, ctx->dev_hdl, ctx->state);

	mutex_unlock(&ctx->ctx_mutex);

	return rc;
}

int cam_context_dump_pf_info(struct cam_context *ctx, unsigned long iova,
	uint32_t buf_info)
{
@@ -524,6 +552,43 @@ int cam_context_handle_info_dump(void *context,
	return rc;
}

int cam_context_handle_dump_dev(struct cam_context *ctx,
	struct cam_dump_req_cmd *cmd)
{
	int rc = 0;

	if (!ctx) {
		CAM_ERR(CAM_CORE, "Invalid Context");
		return -EINVAL;
	}

	if (!ctx->state_machine) {
		CAM_ERR(CAM_CORE, "Context %s ctx_id %d is not ready",
			ctx->dev_name, ctx->ctx_id);
		return -EINVAL;
	}

	if (!cmd) {
		CAM_ERR(CAM_CORE,
			"Context %s ctx_id %d Invalid dump command payload",
			ctx->dev_name, ctx->ctx_id);
		return -EINVAL;
	}

	mutex_lock(&ctx->ctx_mutex);
	CAM_DBG(CAM_CORE, "dump device in dev %d, name %s state %d",
		ctx->dev_hdl, ctx->dev_name, ctx->state);
	if (ctx->state_machine[ctx->state].ioctl_ops.dump_dev)
		rc = ctx->state_machine[ctx->state].ioctl_ops.dump_dev(
			ctx, cmd);
	else
		CAM_WARN(CAM_CORE, "No dump device in dev %d, name %s state %d",
			ctx->dev_hdl, ctx->dev_name, ctx->state);
	mutex_unlock(&ctx->ctx_mutex);

	return rc;
}

int cam_context_init(struct cam_context *ctx,
	const char *dev_name,
	uint64_t dev_id,
+49 −0
Original line number Diff line number Diff line
@@ -23,6 +23,12 @@ struct cam_context;
#define CAM_CTX_CFG_MAX              20
#define CAM_CTX_RES_MAX              20

/* max tag  dump header string length*/
#define CAM_CTXT_DUMP_TAG_MAX_LEN 32

/* Number of words to be dumped for context*/
#define CAM_CTXT_DUMP_NUM_WORDS 10

/**
 * enum cam_ctx_state -  context top level states
 *
@@ -86,6 +92,7 @@ struct cam_ctx_request {
 * @flush_dev:             Function pointer for flush device
 * @acquire_hw:            Function pointer for acquire hw
 * @release_hw:            Function pointer for release hw
 * @dump_dev:              Function pointer for dump dev
 *
 */
struct cam_ctx_ioctl_ops {
@@ -103,6 +110,8 @@ struct cam_ctx_ioctl_ops {
			struct cam_flush_dev_cmd *cmd);
	int (*acquire_hw)(struct cam_context *ctx, void *args);
	int (*release_hw)(struct cam_context *ctx, void *args);
	int (*dump_dev)(struct cam_context *ctx,
			struct cam_dump_req_cmd *cmd);
};

/**
@@ -114,6 +123,7 @@ struct cam_ctx_ioctl_ops {
 * @apply_req:             Apply setting for the context
 * @flush_req:             Flush request to remove request ids
 * @process_evt:           Handle event notification from CRM.(optional)
 * @dump_req:              Dump information for the issue request
 *
 */
struct cam_ctx_crm_ops {
@@ -129,6 +139,8 @@ struct cam_ctx_crm_ops {
			struct cam_req_mgr_flush_request *flush);
	int (*process_evt)(struct cam_context *ctx,
			struct cam_req_mgr_link_evt_data *evt_data);
	int (*dump_req)(struct cam_context *ctx,
			struct cam_req_mgr_dump_info *dump);
};


@@ -219,6 +231,19 @@ struct cam_context {
	uint32_t                     last_flush_req;
};

/**
 * struct cam_context_dump_header -  Function for context dump header
 *
 * @tag         :    Tag for context dump header
 * @size        :    Size of data
 * @word_size   :    Word size of data
 */
struct cam_context_dump_header {
	uint8_t   tag[CAM_CTXT_DUMP_TAG_MAX_LEN];
	uint64_t  size;
	uint32_t  word_size;
};

/**
 * cam_context_shutdown()
 *
@@ -301,6 +326,18 @@ int cam_context_handle_crm_flush_req(struct cam_context *ctx,
int cam_context_handle_crm_process_evt(struct cam_context *ctx,
	struct cam_req_mgr_link_evt_data *process_evt);

/**
 * cam_context_handle_crm_dump_req()
 *
 * @brief:        Handle CRM dump request
 *
 * @ctx:          Object pointer for cam_context
 * @dump:         Dump request command payload
 *
 */
int cam_context_handle_crm_dump_req(struct cam_context *ctx,
	struct cam_req_mgr_dump_info *dump);

/**
 * cam_context_dump_pf_info()
 *
@@ -410,6 +447,18 @@ int cam_context_handle_start_dev(struct cam_context *ctx,
int cam_context_handle_stop_dev(struct cam_context *ctx,
		struct cam_start_stop_dev_cmd *cmd);

/**
 * cam_context_handle_dump_dev()
 *
 * @brief:        Handle dump device command
 *
 * @ctx:          Object pointer for cam_context
 * @cmd:          Dump device command payload
 *
 */
int cam_context_handle_dump_dev(struct cam_context *ctx,
	struct cam_dump_req_cmd *cmd);

/**
 * cam_context_handle_info_dump()
 *
+127 −0
Original line number Diff line number Diff line
@@ -1046,3 +1046,130 @@ int32_t cam_context_dump_hw_acq_info(struct cam_context *ctx)
end:
	return rc;
}

static int cam_context_dump_context(struct cam_context *ctx,
	struct cam_hw_dump_args *dump_args)
{
	int                             rc;
	int                             i;
	size_t                          buf_len;
	size_t                          remain_len;
	uint8_t                        *dst;
	uint64_t                       *addr, *start;
	uint32_t                        min_len;
	uintptr_t                       cpu_addr;
	struct cam_ctx_request         *req;
	struct cam_context_dump_header *hdr;

	if (!ctx || !dump_args) {
		CAM_ERR(CAM_CORE, "Invalid parameters %pK %pK",
			ctx, dump_args);
		return -EINVAL;
	}

	spin_lock_bh(&ctx->lock);
	if (list_empty(&ctx->active_req_list)) {
		CAM_ERR(CAM_CTXT, "[%s][%d] no active request",
			ctx->dev_name, ctx->ctx_id);
		spin_unlock_bh(&ctx->lock);
		return -EIO;
	}
	req = list_first_entry(&ctx->active_req_list,
		struct cam_ctx_request, list);
	spin_unlock_bh(&ctx->lock);
	rc  = cam_mem_get_cpu_buf(dump_args->buf_handle,
		&cpu_addr, &buf_len);
	if (rc) {
		CAM_ERR(CAM_CTXT, "Invalid hdl %u rc %d",
			dump_args->buf_handle, rc);
		return rc;
	}
	if (dump_args->offset >= buf_len) {
		CAM_WARN(CAM_CTXT, "dump buffer overshoot offset %zu len %zu",
			dump_args->offset, buf_len);
		return -ENOSPC;
	}

	remain_len = buf_len - dump_args->offset;
	min_len =  sizeof(struct cam_context_dump_header) +
		    (CAM_CTXT_DUMP_NUM_WORDS + req->num_in_map_entries +
		    (req->num_out_map_entries * 2)) * sizeof(uint64_t);

	if (remain_len < min_len) {
		CAM_WARN(CAM_CTXT, "dump buffer exhaust remain %zu min %u",
			remain_len, min_len);
		return -ENOSPC;
	}
	dst = (uint8_t *)cpu_addr + dump_args->offset;
	hdr = (struct cam_context_dump_header *)dst;
	scnprintf(hdr->tag, CAM_CTXT_DUMP_TAG_MAX_LEN,
		"%s_CTXT_DUMP:", ctx->dev_name);
	hdr->word_size = sizeof(uint64_t);
	addr = (uint64_t *)(dst + sizeof(struct cam_context_dump_header));
	start = addr;
	*addr++ = ctx->ctx_id;
	*addr++ = refcount_read(&(ctx->refcount.refcount));
	*addr++ = ctx->last_flush_req;
	*addr++ = ctx->state;
	*addr++ = req->num_out_map_entries;
	for (i = 0; i < req->num_out_map_entries; i++) {
		*addr++ = req->out_map_entries[i].resource_handle;
		*addr++ = req->out_map_entries[i].sync_id;
	}
	*addr++ = req->num_in_map_entries;
	for (i = 0; i < req->num_in_map_entries; i++)
		*addr++ = req->in_map_entries[i].sync_id;
	hdr->size = hdr->word_size * (addr - start);
	dump_args->offset += hdr->size +
		sizeof(struct cam_context_dump_header);
	return rc;
}

int32_t cam_context_dump_dev_to_hw(struct cam_context *ctx,
	struct cam_dump_req_cmd *cmd)
{
	int                     rc = 0;
	struct cam_hw_dump_args dump_args;

	if (!ctx || !cmd) {
		CAM_ERR(CAM_CTXT, "Invalid input params %pK %pK", ctx, cmd);
		return -EINVAL;
	}
	if (!ctx->hw_mgr_intf) {
		CAM_ERR(CAM_CTXT, "[%s][%d] HW interface is not ready",
			ctx->dev_name, ctx->ctx_id);
		return -EFAULT;
	}
	memset(&dump_args, 0, sizeof(dump_args));
	if (ctx->hw_mgr_intf->hw_dump) {
		dump_args.ctxt_to_hw_map = ctx->ctxt_to_hw_map;
		dump_args.buf_handle = cmd->buf_handle;
		dump_args.offset = cmd->offset;
		dump_args.request_id = cmd->issue_req_id;
		dump_args.error_type = cmd->error_type;
		rc  = ctx->hw_mgr_intf->hw_dump(
			ctx->hw_mgr_intf->hw_mgr_priv,
			&dump_args);
		if (rc) {
			CAM_ERR(CAM_CTXT, "[%s][%d] handle[%u] failed",
			    ctx->dev_name, ctx->ctx_id, dump_args.buf_handle);
			return rc;
		}
		/* Offset will change if the issue request id is found with
		 * the hw and has been lying with it beyond threshold time.
		 * If offset does not change, do not dump the context
		 * information as the current context has no problem with
		 * the provided request id.
		 */
		if (dump_args.offset > cmd->offset) {
			cam_context_dump_context(ctx, &dump_args);
			CAM_INFO(CAM_CTXT, "[%s] ctx: %d Filled Length %u",
				 ctx->dev_name, ctx->ctx_id,
				 dump_args.offset - cmd->offset);
			cmd->offset  = dump_args.offset;
		}
	} else {
		CAM_DBG(CAM_CTXT, "%s hw dump not registered", ctx->dev_name);
	}
	return rc;
}
+2 −1
Original line number Diff line number Diff line
@@ -30,5 +30,6 @@ int32_t cam_context_dump_pf_info_to_hw(struct cam_context *ctx,
	struct cam_packet *packet, unsigned long iova, uint32_t buf_info,
	bool *mem_found);
int32_t cam_context_dump_hw_acq_info(struct cam_context *ctx);

int32_t cam_context_dump_dev_to_hw(struct cam_context *ctx,
	struct cam_dump_req_cmd *cmd);
#endif /* _CAM_CONTEXT_UTILS_H_ */
+19 −0
Original line number Diff line number Diff line
@@ -304,6 +304,23 @@ struct cam_hw_reset_args {
	void                           *ctxt_to_hw_map;
};

/**
 * struct cam_hw_dump_args - Dump arguments
 *
 * @request_id:            request_id
 * @offset:                Buffer offset. This is updated by the drivers.
 * @buf_handle:            Buffer handle
 * @error_type:            Error type, to be used to extend dump information
 * @ctxt_to_hw_map:        HW context from the acquire
 */
struct cam_hw_dump_args {
	uint64_t          request_id;
	size_t            offset;
	uint32_t          buf_handle;
	uint32_t          error_type;
	void             *ctxt_to_hw_map;
};

/* enum cam_hw_mgr_command - Hardware manager command type */
enum cam_hw_mgr_command {
	CAM_HW_MGR_CMD_INTERNAL,
@@ -359,6 +376,7 @@ struct cam_hw_cmd_args {
 * @hw_close:                  Function pointer for HW deinit
 * @hw_flush:                  Function pointer for HW flush
 * @hw_reset:                  Function pointer for HW reset
 * @hw_dump:                   Function pointer for HW dump
 *
 */
struct cam_hw_mgr_intf {
@@ -380,6 +398,7 @@ struct cam_hw_mgr_intf {
	int (*hw_close)(void *hw_priv, void *hw_close_args);
	int (*hw_flush)(void *hw_priv, void *hw_flush_args);
	int (*hw_reset)(void *hw_priv, void *hw_reset_args);
	int (*hw_dump)(void *hw_priv, void *hw_dump_args);
};

#endif /* _CAM_HW_MGR_INTF_H_ */
Loading