msm: mdss: hdcp2p2: fix deadlock during re-authentication

Fix the dead lock happening during re-authentication where
threads were waiting for each other during clearing the last
session and starting a new one.

Change-Id: Ife18adde8349acb92b22e228d0bbc18edbf2c90e
Signed-off-by: Ajay Singh Parmar <aparmar@codeaurora.org>
This commit is contained in:
Ajay Singh Parmar 2016-10-20 17:13:19 -07:00
parent 821b280d6a
commit 01cb3026c6
6 changed files with 141 additions and 131 deletions

View file

@ -616,32 +616,53 @@ static int hdcp_lib_get_next_message(struct hdcp_lib_handle *handle,
}
}
static inline void hdcp_lib_wakeup_client(struct hdcp_lib_handle *handle,
struct hdmi_hdcp_wakeup_data *data)
static void hdcp_lib_wakeup_client(struct hdcp_lib_handle *handle,
struct hdmi_hdcp_wakeup_data *data)
{
int rc = 0;
int rc = 0, i;
if (handle && handle->client_ops && handle->client_ops->wakeup &&
data && (data->cmd != HDMI_HDCP_WKUP_CMD_INVALID)) {
data->abort_mask = REAUTH_REQ | LINK_INTEGRITY_FAILURE;
if (!handle || !handle->client_ops || !handle->client_ops->wakeup ||
!data || (data->cmd == HDMI_HDCP_WKUP_CMD_INVALID))
return;
if (data->cmd == HDMI_HDCP_WKUP_CMD_SEND_MESSAGE ||
data->cmd == HDMI_HDCP_WKUP_CMD_RECV_MESSAGE ||
data->cmd == HDMI_HDCP_WKUP_CMD_LINK_POLL) {
handle->last_msg =
hdcp_lib_get_next_message(handle, data);
data->abort_mask = REAUTH_REQ | LINK_INTEGRITY_FAILURE;
if (handle->last_msg > INVALID_MESSAGE_ID &&
handle->last_msg < HDCP2P2_MAX_MESSAGES)
data->message_data =
&hdcp_msg_lookup[handle->last_msg];
if (data->cmd == HDMI_HDCP_WKUP_CMD_SEND_MESSAGE ||
data->cmd == HDMI_HDCP_WKUP_CMD_RECV_MESSAGE ||
data->cmd == HDMI_HDCP_WKUP_CMD_LINK_POLL) {
handle->last_msg = hdcp_lib_get_next_message(handle, data);
pr_debug("lib->client: %s (%s)\n",
hdmi_hdcp_cmd_to_str(data->cmd),
hdcp_lib_message_name(handle->last_msg));
if (handle->last_msg > INVALID_MESSAGE_ID &&
handle->last_msg < HDCP2P2_MAX_MESSAGES) {
u32 msg_num, rx_status;
const struct hdcp_msg_part *msg;
data->message_data = &hdcp_msg_lookup[handle->last_msg];
msg_num = data->message_data->num_messages;
msg = data->message_data->messages;
rx_status = data->message_data->rx_status;
pr_debug("rxstatus 0x%x\n", rx_status);
pr_debug("%6s | %4s\n", "offset", "len");
for (i = 0; i < msg_num; i++)
pr_debug("%6x | %4d\n",
msg[i].offset, msg[i].length);
}
rc = handle->client_ops->wakeup(data);
if (rc)
pr_err("error sending %s to client\n",
hdmi_hdcp_cmd_to_str(data->cmd));
} else {
pr_debug("lib->client: %s\n",
hdmi_hdcp_cmd_to_str(data->cmd));
}
rc = handle->client_ops->wakeup(data);
if (rc)
pr_err("error sending %s to client\n",
hdmi_hdcp_cmd_to_str(data->cmd));
}
static inline void hdcp_lib_send_message(struct hdcp_lib_handle *handle)
@ -1486,6 +1507,7 @@ static int hdcp_lib_check_valid_state(struct hdcp_lib_handle *handle)
if (handle->wakeup_cmd == HDCP_LIB_WKUP_CMD_START) {
if (!list_empty(&handle->worker.work_list)) {
pr_debug("error: queue not empty\n");
rc = -EBUSY;
goto exit;
}
@ -1543,9 +1565,8 @@ static int hdcp_lib_wakeup(struct hdcp_lib_wakeup_data *data)
handle->wakeup_cmd = data->cmd;
handle->timeout_left = data->timeout;
pr_debug("%s, timeout left: %dms, tethered %d\n",
hdcp_lib_cmd_to_str(handle->wakeup_cmd),
handle->timeout_left, handle->tethered);
pr_debug("client->lib: %s\n",
hdcp_lib_cmd_to_str(handle->wakeup_cmd));
rc = hdcp_lib_check_valid_state(handle);
if (rc)
@ -1599,6 +1620,8 @@ static int hdcp_lib_wakeup(struct hdcp_lib_wakeup_data *data)
break;
case HDCP_LIB_WKUP_CMD_MSG_SEND_FAILED:
case HDCP_LIB_WKUP_CMD_MSG_RECV_FAILED:
case HDCP_LIB_WKUP_CMD_LINK_FAILED:
handle->hdcp_state |= HDCP_STATE_ERROR;
HDCP_LIB_EXECUTE(clean);
break;
case HDCP_LIB_WKUP_CMD_MSG_RECV_SUCCESS:
@ -1825,7 +1848,7 @@ static void hdcp_lib_clean(struct hdcp_lib_handle *handle)
if (!handle) {
pr_err("invalid input\n");
return;
};
}
hdcp_lib_txmtr_deinit(handle);
if (!handle->legacy_app)
@ -1859,6 +1882,7 @@ static void hdcp_lib_msg_recvd(struct hdcp_lib_handle *handle)
struct hdcp_rcvd_msg_rsp *rsp_buf;
uint32_t msglen;
char *msg = NULL;
char msg_name[50];
uint32_t message_id_bytes = 0;
if (!handle || !handle->qseecom_handle ||
@ -1907,8 +1931,11 @@ static void hdcp_lib_msg_recvd(struct hdcp_lib_handle *handle)
mutex_unlock(&handle->msg_lock);
pr_debug("msg received: %s from sink\n",
hdcp_lib_message_name((int)msg[0]));
snprintf(msg_name, sizeof(msg_name), "%s: ",
hdcp_lib_message_name((int)msg[0]));
print_hex_dump(KERN_DEBUG, msg_name,
DUMP_PREFIX_NONE, 16, 1, msg, msglen, false);
/* send the message to QSEECOM */
req_buf = (struct hdcp_rcvd_msg_req *)(handle->qseecom_handle->sbuf);
@ -2026,6 +2053,16 @@ static void hdcp_lib_topology_work(struct kthread_work *work)
return;
}
if (atomic_read(&handle->hdcp_off)) {
pr_debug("invalid state: hdcp off\n");
return;
}
if (handle->hdcp_state & HDCP_STATE_ERROR) {
pr_debug("invalid state: hdcp error\n");
return;
}
reinit_completion(&handle->topo_wait);
timeout = wait_for_completion_timeout(&handle->topo_wait, HZ * 3);
if (!timeout) {

View file

@ -1620,22 +1620,18 @@ end:
return rc;
}
static void mdss_dp_hdcp_cb(void *ptr, enum hdcp_states status)
static void mdss_dp_hdcp_cb_work(struct work_struct *work)
{
struct mdss_dp_drv_pdata *dp = ptr;
struct mdss_dp_drv_pdata *dp;
struct delayed_work *dw = to_delayed_work(work);
struct hdcp_ops *ops;
int rc = 0;
if (!dp) {
pr_debug("invalid input\n");
return;
}
dp = container_of(dw, struct mdss_dp_drv_pdata, hdcp_cb_work);
ops = dp->hdcp.ops;
mutex_lock(&dp->train_mutex);
switch (status) {
switch (dp->hdcp_status) {
case HDCP_STATE_AUTHENTICATED:
pr_debug("hdcp authenticated\n");
dp->hdcp.auth_state = true;
@ -1658,8 +1654,20 @@ static void mdss_dp_hdcp_cb(void *ptr, enum hdcp_states status)
default:
break;
}
}
mutex_unlock(&dp->train_mutex);
static void mdss_dp_hdcp_cb(void *ptr, enum hdcp_states status)
{
struct mdss_dp_drv_pdata *dp = ptr;
if (!dp) {
pr_err("invalid input\n");
return;
}
dp->hdcp_status = status;
queue_delayed_work(dp->workq, &dp->hdcp_cb_work, HZ/4);
}
static int mdss_dp_hdcp_init(struct mdss_panel_data *pdata)
@ -1888,6 +1896,13 @@ static void mdss_dp_update_hdcp_info(struct mdss_dp_drv_pdata *dp)
dp->hdcp.ops = ops;
}
static inline bool dp_is_hdcp_enabled(struct mdss_dp_drv_pdata *dp_drv)
{
return dp_drv->hdcp.feature_enabled &&
(dp_drv->hdcp.hdcp1_present || dp_drv->hdcp.hdcp2_present) &&
dp_drv->hdcp.ops;
}
static int mdss_dp_event_handler(struct mdss_panel_data *pdata,
int event, void *arg)
{
@ -1919,8 +1934,10 @@ static int mdss_dp_event_handler(struct mdss_panel_data *pdata,
rc = mdss_dp_off(pdata);
break;
case MDSS_EVENT_BLANK:
if (dp->hdcp.ops && dp->hdcp.ops->off)
if (dp_is_hdcp_enabled(dp) && dp->hdcp.ops->off) {
flush_delayed_work(&dp->hdcp_cb_work);
dp->hdcp.ops->off(dp->hdcp.data);
}
mdss_dp_mainlink_push_idle(pdata);
break;
@ -2197,6 +2214,11 @@ irqreturn_t dp_isr(int irq, void *ptr)
dp_aux_native_handler(dp, isr1);
}
if (dp->hdcp.ops && dp->hdcp.ops->isr) {
if (dp->hdcp.ops->isr(dp->hdcp.data))
pr_err("dp_hdcp_isr failed\n");
}
return IRQ_HANDLED;
}
@ -2211,6 +2233,7 @@ static int mdss_dp_event_setup(struct mdss_dp_drv_pdata *dp)
}
INIT_WORK(&dp->work, mdss_dp_event_work);
INIT_DELAYED_WORK(&dp->hdcp_cb_work, mdss_dp_hdcp_cb_work);
return 0;
}
@ -2434,8 +2457,6 @@ static void mdss_dp_process_hpd_irq_high(struct mdss_dp_drv_pdata *dp)
{
int ret = 0;
pr_debug("enter: HPD IRQ High\n");
dp->hpd_irq_on = true;
mdss_dp_aux_parse_sink_status_field(dp);
@ -2523,6 +2544,12 @@ static void usbpd_response_callback(struct usbpd_svid_handler *hdlr, u8 cmd,
dp_drv->alt_mode.dp_status.hpd_irq;
if (dp_drv->alt_mode.dp_status.hpd_irq) {
pr_debug("Attention: hpd_irq high\n");
if (dp_drv->power_on && dp_drv->hdcp.ops &&
dp_drv->hdcp.ops->cp_irq)
dp_drv->hdcp.ops->cp_irq(dp_drv->hdcp.data);
mdss_dp_process_hpd_irq_high(dp_drv);
break;
}
@ -2552,9 +2579,6 @@ static void usbpd_response_callback(struct usbpd_svid_handler *hdlr, u8 cmd,
else
dp_send_events(dp_drv, EV_USBPD_DP_CONFIGURE);
if (dp_drv->alt_mode.dp_status.hpd_irq && dp_drv->power_on &&
dp_drv->hdcp.ops && dp_drv->hdcp.ops->isr)
dp_drv->hdcp.ops->isr(dp_drv->hdcp.data);
break;
case DP_VDM_STATUS:
dp_drv->alt_mode.dp_status.response = *vdos;
@ -2770,13 +2794,6 @@ void *mdss_dp_get_hdcp_data(struct device *dev)
return dp_drv->hdcp.data;
}
static inline bool dp_is_hdcp_enabled(struct mdss_dp_drv_pdata *dp_drv)
{
return dp_drv->hdcp.feature_enabled &&
(dp_drv->hdcp.hdcp1_present || dp_drv->hdcp.hdcp2_present) &&
dp_drv->hdcp.ops;
}
static inline bool dp_is_stream_shareable(struct mdss_dp_drv_pdata *dp_drv)
{
bool ret = 0;

View file

@ -484,6 +484,7 @@ struct mdss_dp_drv_pdata {
/* event */
struct workqueue_struct *workq;
struct work_struct work;
struct delayed_work hdcp_cb_work;
u32 current_event;
spinlock_t event_lock;
spinlock_t lock;
@ -494,6 +495,7 @@ struct mdss_dp_drv_pdata {
u32 vic;
u32 new_vic;
int fb_node;
int hdcp_status;
struct dpcd_test_request test_data;
struct dpcd_sink_count sink_count;

View file

@ -53,7 +53,6 @@ struct dp_hdcp2p2_ctrl {
struct kthread_work send_msg;
struct kthread_work recv_msg;
struct kthread_work link;
struct kthread_work poll;
char *msg_buf;
uint32_t send_msg_len; /* length of all parameters in msg */
uint32_t timeout;
@ -67,26 +66,6 @@ struct dp_hdcp2p2_ctrl {
bool polling;
};
static inline char *dp_hdcp_cmd_to_str(uint32_t cmd)
{
switch (cmd) {
case HDMI_HDCP_WKUP_CMD_SEND_MESSAGE:
return "HDMI_HDCP_WKUP_CMD_SEND_MESSAGE";
case HDMI_HDCP_WKUP_CMD_RECV_MESSAGE:
return "HDMI_HDCP_WKUP_CMD_RECV_MESSAGE";
case HDMI_HDCP_WKUP_CMD_STATUS_SUCCESS:
return "HDMI_HDCP_WKUP_CMD_STATUS_SUCCESS";
case HDMI_HDCP_WKUP_CMD_STATUS_FAILED:
return "DP_HDCP_WKUP_CMD_STATUS_FAIL";
case HDMI_HDCP_WKUP_CMD_LINK_POLL:
return "HDMI_HDCP_WKUP_CMD_LINK_POLL";
case HDMI_HDCP_WKUP_CMD_AUTHENTICATE:
return "HDMI_HDCP_WKUP_CMD_AUTHENTICATE";
default:
return "???";
}
}
static inline bool dp_hdcp2p2_is_valid_state(struct dp_hdcp2p2_ctrl *ctrl)
{
if (ctrl->wakeup_cmd == HDMI_HDCP_WKUP_CMD_AUTHENTICATE)
@ -193,7 +172,7 @@ static int dp_hdcp2p2_wakeup(struct hdmi_hdcp_wakeup_data *data)
queue_kthread_work(&ctrl->worker, &ctrl->status);
break;
case HDMI_HDCP_WKUP_CMD_LINK_POLL:
queue_kthread_work(&ctrl->worker, &ctrl->poll);
ctrl->polling = true;
break;
case HDMI_HDCP_WKUP_CMD_AUTHENTICATE:
queue_kthread_work(&ctrl->worker, &ctrl->auth);
@ -203,10 +182,11 @@ static int dp_hdcp2p2_wakeup(struct hdmi_hdcp_wakeup_data *data)
}
exit:
mutex_unlock(&ctrl->wakeup_mutex);
return 0;
}
static inline int dp_hdcp2p2_wakeup_lib(struct dp_hdcp2p2_ctrl *ctrl,
static inline void dp_hdcp2p2_wakeup_lib(struct dp_hdcp2p2_ctrl *ctrl,
struct hdcp_lib_wakeup_data *data)
{
int rc = 0;
@ -218,8 +198,6 @@ static inline int dp_hdcp2p2_wakeup_lib(struct dp_hdcp2p2_ctrl *ctrl,
pr_err("error sending %s to lib\n",
hdcp_lib_cmd_to_str(data->cmd));
}
return rc;
}
static void dp_hdcp2p2_reset(struct dp_hdcp2p2_ctrl *ctrl)
@ -339,8 +317,6 @@ static void dp_hdcp2p2_auth_failed(struct dp_hdcp2p2_ctrl *ctrl)
return;
}
atomic_set(&ctrl->auth_state, HDCP_STATE_AUTH_FAIL);
/* notify DP about HDCP failure */
ctrl->init_data.notify_status(ctrl->init_data.cb_data,
HDCP_STATE_AUTH_FAIL);
@ -349,8 +325,7 @@ static void dp_hdcp2p2_auth_failed(struct dp_hdcp2p2_ctrl *ctrl)
static int dp_hdcp2p2_aux_read_message(struct dp_hdcp2p2_ctrl *ctrl,
u8 *buf, int size, int offset, u32 timeout)
{
int rc, max_size = 16, read_size, len = size;
u8 *buf_start = buf;
int rc, max_size = 16, read_size;
if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) {
pr_err("hdcp is off\n");
@ -378,8 +353,6 @@ static int dp_hdcp2p2_aux_read_message(struct dp_hdcp2p2_ctrl *ctrl,
size -= read_size;
} while (size > 0);
print_hex_dump(KERN_DEBUG, "hdcp2p2: ", DUMP_PREFIX_NONE,
16, 1, buf_start, len, false);
return rc;
}
@ -550,7 +523,6 @@ exit:
static void dp_hdcp2p2_recv_msg_work(struct kthread_work *work)
{
int rc = 0;
struct hdcp_lib_wakeup_data cdata = { HDCP_LIB_WKUP_CMD_INVALID };
struct dp_hdcp2p2_ctrl *ctrl = container_of(work,
struct dp_hdcp2p2_ctrl, recv_msg);
@ -559,44 +531,24 @@ static void dp_hdcp2p2_recv_msg_work(struct kthread_work *work)
if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) {
pr_err("hdcp is off\n");
goto exit;
return;
}
if (ctrl->sink_rx_status & ctrl->abort_mask) {
pr_err("reauth or Link fail triggered by sink\n");
if (ctrl->rx_status) {
if (!ctrl->cp_irq_done) {
pr_debug("waiting for CP_IRQ\n");
ctrl->polling = true;
return;
}
ctrl->sink_rx_status = 0;
rc = -ENOLINK;
cdata.cmd = HDCP_LIB_WKUP_CMD_STOP;
goto exit;
}
if (ctrl->rx_status && !ctrl->sink_rx_status) {
pr_debug("Recv msg for RxStatus, but no CP_IRQ yet\n");
ctrl->polling = true;
goto exit;
if (ctrl->rx_status & ctrl->sink_rx_status) {
ctrl->cp_irq_done = false;
ctrl->sink_rx_status = 0;
ctrl->rx_status = 0;
}
}
dp_hdcp2p2_get_msg_from_sink(ctrl);
return;
exit:
if (rc)
dp_hdcp2p2_wakeup_lib(ctrl, &cdata);
}
static void dp_hdcp2p2_poll_work(struct kthread_work *work)
{
struct dp_hdcp2p2_ctrl *ctrl = container_of(work,
struct dp_hdcp2p2_ctrl, poll);
if (ctrl->cp_irq_done) {
ctrl->cp_irq_done = false;
dp_hdcp2p2_get_msg_from_sink(ctrl);
} else {
ctrl->polling = true;
}
}
static void dp_hdcp2p2_auth_status_work(struct kthread_work *work)
@ -645,44 +597,45 @@ static void dp_hdcp2p2_link_work(struct kthread_work *work)
if (rc) {
pr_err("failed to read rx status\n");
cdata.cmd = HDCP_LIB_WKUP_CMD_STOP;
cdata.cmd = HDCP_LIB_WKUP_CMD_LINK_FAILED;
atomic_set(&ctrl->auth_state, HDCP_STATE_AUTH_FAIL);
goto exit;
}
if (ctrl->sink_rx_status & ctrl->abort_mask) {
pr_err("reauth or Link fail triggered by sink\n");
if (ctrl->sink_rx_status & BIT(3))
pr_err("reauth_req set by sink\n");
if (ctrl->sink_rx_status & BIT(4))
pr_err("link failure reported by sink\n");
ctrl->sink_rx_status = 0;
ctrl->rx_status = 0;
rc = -ENOLINK;
cdata.cmd = HDCP_LIB_WKUP_CMD_STOP;
cdata.cmd = HDCP_LIB_WKUP_CMD_LINK_FAILED;
atomic_set(&ctrl->auth_state, HDCP_STATE_AUTH_FAIL);
goto exit;
}
/* if polling, get message from sink else let polling start */
if (ctrl->polling && (ctrl->sink_rx_status & ctrl->rx_status)) {
ctrl->sink_rx_status = 0;
ctrl->rx_status = 0;
rc = dp_hdcp2p2_get_msg_from_sink(ctrl);
dp_hdcp2p2_get_msg_from_sink(ctrl);
ctrl->polling = false;
} else {
ctrl->cp_irq_done = true;
}
exit:
dp_hdcp2p2_wakeup_lib(ctrl, &cdata);
if (rc) {
dp_hdcp2p2_auth_failed(ctrl);
return;
}
if (rc)
dp_hdcp2p2_wakeup_lib(ctrl, &cdata);
}
static void dp_hdcp2p2_auth_work(struct kthread_work *work)
{
int rc = 0;
struct hdcp_lib_wakeup_data cdata = {HDCP_LIB_WKUP_CMD_INVALID};
struct dp_hdcp2p2_ctrl *ctrl = container_of(work,
struct dp_hdcp2p2_ctrl, auth);
@ -694,12 +647,10 @@ static void dp_hdcp2p2_auth_work(struct kthread_work *work)
else
cdata.cmd = HDCP_LIB_WKUP_CMD_STOP;
rc = dp_hdcp2p2_wakeup_lib(ctrl, &cdata);
if (rc)
dp_hdcp2p2_auth_failed(ctrl);
dp_hdcp2p2_wakeup_lib(ctrl, &cdata);
}
static int dp_hdcp2p2_isr(void *input)
static int dp_hdcp2p2_cp_irq(void *input)
{
struct dp_hdcp2p2_ctrl *ctrl = input;
@ -748,7 +699,7 @@ void *dp_hdcp2p2_init(struct hdcp_init_data *init_data)
.authenticate = dp_hdcp2p2_authenticate,
.feature_supported = dp_hdcp2p2_feature_supported,
.off = dp_hdcp2p2_off,
.isr = dp_hdcp2p2_isr
.cp_irq = dp_hdcp2p2_cp_irq,
};
static struct hdcp_client_ops client_ops = {
@ -806,7 +757,6 @@ void *dp_hdcp2p2_init(struct hdcp_init_data *init_data)
init_kthread_work(&ctrl->recv_msg, dp_hdcp2p2_recv_msg_work);
init_kthread_work(&ctrl->status, dp_hdcp2p2_auth_status_work);
init_kthread_work(&ctrl->link, dp_hdcp2p2_link_work);
init_kthread_work(&ctrl->poll, dp_hdcp2p2_poll_work);
ctrl->thread = kthread_run(kthread_worker_fn,
&ctrl->worker, "dp_hdcp2p2");

View file

@ -55,6 +55,7 @@ struct hdcp_init_data {
struct hdcp_ops {
int (*isr)(void *ptr);
int (*cp_irq)(void *ptr);
int (*reauthenticate)(void *input);
int (*authenticate)(void *hdcp_ctrl);
bool (*feature_supported)(void *input);

View file

@ -26,6 +26,7 @@ enum hdcp_lib_wakeup_cmd {
HDCP_LIB_WKUP_CMD_MSG_RECV_FAILED,
HDCP_LIB_WKUP_CMD_MSG_RECV_TIMEOUT,
HDCP_LIB_WKUP_CMD_QUERY_STREAM_TYPE,
HDCP_LIB_WKUP_CMD_LINK_FAILED,
};
enum hdmi_hdcp_wakeup_cmd {
@ -106,6 +107,8 @@ static inline char *hdcp_lib_cmd_to_str(uint32_t cmd)
return "HDCP_LIB_WKUP_CMD_MSG_RECV_TIMEOUT";
case HDCP_LIB_WKUP_CMD_QUERY_STREAM_TYPE:
return "HDCP_LIB_WKUP_CMD_QUERY_STREAM_TYPE";
case HDCP_LIB_WKUP_CMD_LINK_FAILED:
return "HDCP_LIB_WKUP_CMD_LINK_FAILED";
default:
return "???";
}