From patchwork Wed Mar 9 15:57:21 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hans Verkuil X-Patchwork-Id: 549955 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id D3200C433F5 for ; Wed, 9 Mar 2022 15:57:41 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S234081AbiCIP6i (ORCPT ); Wed, 9 Mar 2022 10:58:38 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54628 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234079AbiCIP6e (ORCPT ); Wed, 9 Mar 2022 10:58:34 -0500 Received: from ams.source.kernel.org (ams.source.kernel.org [145.40.68.75]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id DA56847AE7 for ; Wed, 9 Mar 2022 07:57:32 -0800 (PST) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ams.source.kernel.org (Postfix) with ESMTPS id 63BBBB8221C for ; Wed, 9 Mar 2022 15:57:31 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 68807C340EF; Wed, 9 Mar 2022 15:57:29 +0000 (UTC) From: Hans Verkuil To: linux-media@vger.kernel.org Cc: Hans Verkuil Subject: [PATCH 1/6] cec: call enable_adap on s_log_addrs Date: Wed, 9 Mar 2022 16:57:21 +0100 Message-Id: <20220309155726.1258388-2-hverkuil-cisco@xs4all.nl> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220309155726.1258388-1-hverkuil-cisco@xs4all.nl> References: <20220309155726.1258388-1-hverkuil-cisco@xs4all.nl> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org Don't enable/disable the adapter if the first fh is opened or the last fh is closed, instead do this when the adapter is configured or unconfigured. However, if needs_hpd is true, then do this when the physical address is set or cleared: in that case the adapter typically is powered by the HPD, so it really is disabled when the HPD is low. This case (needs_hpd is true) was already handled in this way, so this wasn't changed. The problem with the old behavior was that if the HPD goes low when no fh is open, and a transmit was in progress, then the adapter would be disabled, typically stopping the transmit immediately which leaves a partial message on the bus, which isn't nice and can confuse some adapters. It makes much more sense to disable it only when the adapter is unconfigured, since then you really won't be using it anymore. Signed-off-by: Hans Verkuil --- drivers/media/cec/core/cec-adap.c | 38 ++++++++++++++++++++++++------- drivers/media/cec/core/cec-api.c | 18 +-------------- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c index 2e12331c12a9..65eae7f3f49b 100644 --- a/drivers/media/cec/core/cec-adap.c +++ b/drivers/media/cec/core/cec-adap.c @@ -1580,24 +1580,24 @@ void __cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block) /* Disabling monitor all mode should always succeed */ if (adap->monitor_all_cnt) WARN_ON(call_op(adap, adap_monitor_all_enable, false)); - /* serialize adap_enable */ - mutex_lock(&adap->devnode.lock); - if (adap->needs_hpd || list_empty(&adap->devnode.fhs)) { + if (adap->needs_hpd) { + /* serialize adap_enable */ + mutex_lock(&adap->devnode.lock); WARN_ON(adap->ops->adap_enable(adap, false)); adap->transmit_in_progress = false; wake_up_interruptible(&adap->kthread_waitq); + mutex_unlock(&adap->devnode.lock); } - mutex_unlock(&adap->devnode.lock); if (phys_addr == CEC_PHYS_ADDR_INVALID) return; } /* serialize adap_enable */ mutex_lock(&adap->devnode.lock); - adap->last_initiator = 0xff; - adap->transmit_in_progress = false; - if (adap->needs_hpd || list_empty(&adap->devnode.fhs)) { + if (adap->needs_hpd) { + adap->last_initiator = 0xff; + adap->transmit_in_progress = false; if (adap->ops->adap_enable(adap, true)) { mutex_unlock(&adap->devnode.lock); return; @@ -1606,8 +1606,10 @@ void __cec_s_phys_addr(struct cec_adapter *adap, u16 phys_addr, bool block) if (adap->monitor_all_cnt && call_op(adap, adap_monitor_all_enable, true)) { - if (adap->needs_hpd || list_empty(&adap->devnode.fhs)) + if (adap->needs_hpd) { WARN_ON(adap->ops->adap_enable(adap, false)); + adap->transmit_in_progress = false; + } mutex_unlock(&adap->devnode.lock); return; } @@ -1683,6 +1685,13 @@ int __cec_s_log_addrs(struct cec_adapter *adap, adap->log_addrs.osd_name[0] = '\0'; adap->log_addrs.vendor_id = CEC_VENDOR_ID_NONE; adap->log_addrs.cec_version = CEC_OP_CEC_VERSION_2_0; + if (!adap->needs_hpd) { + /* serialize adap_enable */ + mutex_lock(&adap->devnode.lock); + WARN_ON(adap->ops->adap_enable(adap, false)); + adap->transmit_in_progress = false; + mutex_unlock(&adap->devnode.lock); + } return 0; } @@ -1816,6 +1825,19 @@ int __cec_s_log_addrs(struct cec_adapter *adap, sizeof(log_addrs->features[i])); } + if (!adap->needs_hpd) { + int ret; + + /* serialize adap_enable */ + mutex_lock(&adap->devnode.lock); + adap->last_initiator = 0xff; + adap->transmit_in_progress = false; + ret = adap->ops->adap_enable(adap, true); + mutex_unlock(&adap->devnode.lock); + + if (ret) + return ret; + } log_addrs->log_addr_mask = adap->log_addrs.log_addr_mask; adap->log_addrs = *log_addrs; if (adap->phys_addr != CEC_PHYS_ADDR_INVALID) diff --git a/drivers/media/cec/core/cec-api.c b/drivers/media/cec/core/cec-api.c index d72ad48c9898..0284db12842b 100644 --- a/drivers/media/cec/core/cec-api.c +++ b/drivers/media/cec/core/cec-api.c @@ -586,18 +586,6 @@ static int cec_open(struct inode *inode, struct file *filp) return err; } - /* serialize adap_enable */ - mutex_lock(&devnode->lock); - if (list_empty(&devnode->fhs) && - !adap->needs_hpd && - adap->phys_addr == CEC_PHYS_ADDR_INVALID) { - err = adap->ops->adap_enable(adap, true); - if (err) { - mutex_unlock(&devnode->lock); - kfree(fh); - return err; - } - } filp->private_data = fh; /* Queue up initial state events */ @@ -625,6 +613,7 @@ static int cec_open(struct inode *inode, struct file *filp) } #endif + mutex_lock(&devnode->lock); mutex_lock(&devnode->lock_fhs); list_add(&fh->list, &devnode->fhs); mutex_unlock(&devnode->lock_fhs); @@ -656,15 +645,10 @@ static int cec_release(struct inode *inode, struct file *filp) cec_monitor_all_cnt_dec(adap); mutex_unlock(&adap->lock); - /* serialize adap_enable */ mutex_lock(&devnode->lock); mutex_lock(&devnode->lock_fhs); list_del(&fh->list); mutex_unlock(&devnode->lock_fhs); - if (cec_is_registered(adap) && list_empty(&devnode->fhs) && - !adap->needs_hpd && adap->phys_addr == CEC_PHYS_ADDR_INVALID) { - WARN_ON(adap->ops->adap_enable(adap, false)); - } mutex_unlock(&devnode->lock); /* Unhook pending transmits from this filehandle. */ From patchwork Wed Mar 9 15:57:22 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hans Verkuil X-Patchwork-Id: 549957 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 59922C433F5 for ; Wed, 9 Mar 2022 15:57:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S234085AbiCIP6f (ORCPT ); Wed, 9 Mar 2022 10:58:35 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54602 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234076AbiCIP6e (ORCPT ); Wed, 9 Mar 2022 10:58:34 -0500 Received: from dfw.source.kernel.org (dfw.source.kernel.org [139.178.84.217]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 36B064926A for ; Wed, 9 Mar 2022 07:57:31 -0800 (PST) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id 862DC61653 for ; Wed, 9 Mar 2022 15:57:31 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 629B4C340F3; Wed, 9 Mar 2022 15:57:30 +0000 (UTC) From: Hans Verkuil To: linux-media@vger.kernel.org Cc: Hans Verkuil Subject: [PATCH 2/6] cec: abort if the current transmit was canceled Date: Wed, 9 Mar 2022 16:57:22 +0100 Message-Id: <20220309155726.1258388-3-hverkuil-cisco@xs4all.nl> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220309155726.1258388-1-hverkuil-cisco@xs4all.nl> References: <20220309155726.1258388-1-hverkuil-cisco@xs4all.nl> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org If a transmit-in-progress was canceled, then, once the transmit is done, mark it as aborted and refrain from retrying the transmit. To signal this situation the new transmit_in_progress_aborted field is set to true. The old implementation would just set adap->transmitting to NULL and set adap->transmit_in_progress to false, but on the hardware level the transmit was still ongoing. However, the framework would think the transmit was aborted, and if a new transmit was issued, then it could overwrite the HW buffer containing the old transmit with the new transmit, leading to garbled data on the CEC bus. Signed-off-by: Hans Verkuil --- drivers/media/cec/core/cec-adap.c | 11 ++++++++--- include/media/cec.h | 6 ++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c index 65eae7f3f49b..b096bf709f10 100644 --- a/drivers/media/cec/core/cec-adap.c +++ b/drivers/media/cec/core/cec-adap.c @@ -421,7 +421,7 @@ static void cec_flush(struct cec_adapter *adap) cec_data_cancel(data, CEC_TX_STATUS_ABORTED); } if (adap->transmitting) - cec_data_cancel(adap->transmitting, CEC_TX_STATUS_ABORTED); + adap->transmit_in_progress_aborted = true; /* Cancel the pending timeout work. */ list_for_each_entry_safe(data, n, &adap->wait_queue, list) { @@ -572,6 +572,7 @@ int cec_thread_func(void *_adap) if (data->attempts == 0) data->attempts = attempts; + adap->transmit_in_progress_aborted = false; /* Tell the adapter to transmit, cancel on error */ if (adap->ops->adap_transmit(adap, data->attempts, signal_free_time, &data->msg)) @@ -599,6 +600,8 @@ void cec_transmit_done_ts(struct cec_adapter *adap, u8 status, struct cec_msg *msg; unsigned int attempts_made = arb_lost_cnt + nack_cnt + low_drive_cnt + error_cnt; + bool done = status & (CEC_TX_STATUS_MAX_RETRIES | CEC_TX_STATUS_OK); + bool aborted = adap->transmit_in_progress_aborted; dprintk(2, "%s: status 0x%02x\n", __func__, status); if (attempts_made < 1) @@ -619,6 +622,7 @@ void cec_transmit_done_ts(struct cec_adapter *adap, u8 status, goto wake_thread; } adap->transmit_in_progress = false; + adap->transmit_in_progress_aborted = false; msg = &data->msg; @@ -639,8 +643,7 @@ void cec_transmit_done_ts(struct cec_adapter *adap, u8 status, * the hardware didn't signal that it retried itself (by setting * CEC_TX_STATUS_MAX_RETRIES), then we will retry ourselves. */ - if (data->attempts > attempts_made && - !(status & (CEC_TX_STATUS_MAX_RETRIES | CEC_TX_STATUS_OK))) { + if (!aborted && data->attempts > attempts_made && !done) { /* Retry this message */ data->attempts -= attempts_made; if (msg->timeout) @@ -655,6 +658,8 @@ void cec_transmit_done_ts(struct cec_adapter *adap, u8 status, goto wake_thread; } + if (aborted && !done) + status |= CEC_TX_STATUS_ABORTED; data->attempts = 0; /* Always set CEC_TX_STATUS_MAX_RETRIES on error */ diff --git a/include/media/cec.h b/include/media/cec.h index 77346f757036..a2a9a5eebae8 100644 --- a/include/media/cec.h +++ b/include/media/cec.h @@ -163,6 +163,11 @@ struct cec_adap_ops { * @wait_queue: queue of transmits waiting for a reply * @transmitting: CEC messages currently being transmitted * @transmit_in_progress: true if a transmit is in progress + * @transmit_in_progress_aborted: true if a transmit is in progress is to be + * aborted. This happens if the logical address is + * invalidated while the transmit is ongoing. In that + * case the transmit will finish, but will not retransmit + * and be marked as ABORTED. * @kthread_config: kthread used to configure a CEC adapter * @config_completion: used to signal completion of the config kthread * @kthread: main CEC processing thread @@ -217,6 +222,7 @@ struct cec_adapter { struct list_head wait_queue; struct cec_data *transmitting; bool transmit_in_progress; + bool transmit_in_progress_aborted; struct task_struct *kthread_config; struct completion config_completion; From patchwork Wed Mar 9 15:57:23 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hans Verkuil X-Patchwork-Id: 549956 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 704B7C433EF for ; Wed, 9 Mar 2022 15:57:39 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S234077AbiCIP6g (ORCPT ); Wed, 9 Mar 2022 10:58:36 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54630 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234078AbiCIP6e (ORCPT ); Wed, 9 Mar 2022 10:58:34 -0500 Received: from dfw.source.kernel.org (dfw.source.kernel.org [139.178.84.217]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id F2F604C431 for ; Wed, 9 Mar 2022 07:57:32 -0800 (PST) Received: from smtp.kernel.org (relay.kernel.org [52.25.139.140]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dfw.source.kernel.org (Postfix) with ESMTPS id 7F40561450 for ; Wed, 9 Mar 2022 15:57:32 +0000 (UTC) Received: by smtp.kernel.org (Postfix) with ESMTPSA id 5C816C340E8; Wed, 9 Mar 2022 15:57:31 +0000 (UTC) From: Hans Verkuil To: linux-media@vger.kernel.org Cc: Hans Verkuil Subject: [PATCH 3/6] cec: correctly pass on reply results Date: Wed, 9 Mar 2022 16:57:23 +0100 Message-Id: <20220309155726.1258388-4-hverkuil-cisco@xs4all.nl> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220309155726.1258388-1-hverkuil-cisco@xs4all.nl> References: <20220309155726.1258388-1-hverkuil-cisco@xs4all.nl> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-media@vger.kernel.org The results of non-blocking transmits were not correctly communicated to userspace. Specifically: 1) if a non-blocking transmit was canceled, then rx_status wasn't set to 0 as it should. 2) if the non-blocking transmit succeeded, but the corresponding reply never arrived (aborted or timed out), then tx_status wasn't set to 0 as it should, and rx_status was hardcoded to ABORTED instead of the actual reason, such as TIMEOUT. In addition, adap->ops->received() was never called, so drivers that want to do message processing themselves would not be informed of the failed reply. Signed-off-by: Hans Verkuil --- drivers/media/cec/core/cec-adap.c | 46 +++++++++++++++++++------------ 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/drivers/media/cec/core/cec-adap.c b/drivers/media/cec/core/cec-adap.c index b096bf709f10..a8735697e84d 100644 --- a/drivers/media/cec/core/cec-adap.c +++ b/drivers/media/cec/core/cec-adap.c @@ -366,38 +366,48 @@ static void cec_data_completed(struct cec_data *data) /* * A pending CEC transmit needs to be cancelled, either because the CEC * adapter is disabled or the transmit takes an impossibly long time to - * finish. + * finish, or the reply timed out. * * This function is called with adap->lock held. */ -static void cec_data_cancel(struct cec_data *data, u8 tx_status) +static void cec_data_cancel(struct cec_data *data, u8 tx_status, u8 rx_status) { + struct cec_adapter *adap = data->adap; + /* * It's either the current transmit, or it is a pending * transmit. Take the appropriate action to clear it. */ - if (data->adap->transmitting == data) { - data->adap->transmitting = NULL; + if (adap->transmitting == data) { + adap->transmitting = NULL; } else { list_del_init(&data->list); if (!(data->msg.tx_status & CEC_TX_STATUS_OK)) - if (!WARN_ON(!data->adap->transmit_queue_sz)) - data->adap->transmit_queue_sz--; + if (!WARN_ON(!adap->transmit_queue_sz)) + adap->transmit_queue_sz--; } if (data->msg.tx_status & CEC_TX_STATUS_OK) { data->msg.rx_ts = ktime_get_ns(); - data->msg.rx_status = CEC_RX_STATUS_ABORTED; + data->msg.rx_status = rx_status; + if (!data->blocking) + data->msg.tx_status = 0; } else { data->msg.tx_ts = ktime_get_ns(); data->msg.tx_status |= tx_status | CEC_TX_STATUS_MAX_RETRIES; data->msg.tx_error_cnt++; data->attempts = 0; + if (!data->blocking) + data->msg.rx_status = 0; } /* Queue transmitted message for monitoring purposes */ - cec_queue_msg_monitor(data->adap, &data->msg, 1); + cec_queue_msg_monitor(adap, &data->msg, 1); + + if (!data->blocking && data->msg.sequence && adap->ops->received) + /* Allow drivers to process the message first */ + adap->ops->received(adap, &data->msg); cec_data_completed(data); } @@ -418,7 +428,7 @@ static void cec_flush(struct cec_adapter *adap) while (!list_empty(&adap->transmit_queue)) { data = list_first_entry(&adap->transmit_queue, struct cec_data, list); - cec_data_cancel(data, CEC_TX_STATUS_ABORTED); + cec_data_cancel(data, CEC_TX_STATUS_ABORTED, 0); } if (adap->transmitting) adap->transmit_in_progress_aborted = true; @@ -426,7 +436,7 @@ static void cec_flush(struct cec_adapter *adap) /* Cancel the pending timeout work. */ list_for_each_entry_safe(data, n, &adap->wait_queue, list) { if (cancel_delayed_work(&data->work)) - cec_data_cancel(data, CEC_TX_STATUS_OK); + cec_data_cancel(data, CEC_TX_STATUS_OK, CEC_RX_STATUS_ABORTED); /* * If cancel_delayed_work returned false, then * the cec_wait_timeout function is running, @@ -516,7 +526,7 @@ int cec_thread_func(void *_adap) adap->transmitting->msg.msg); /* Just give up on this. */ cec_data_cancel(adap->transmitting, - CEC_TX_STATUS_TIMEOUT); + CEC_TX_STATUS_TIMEOUT, 0); } else { pr_warn("cec-%s: transmit timed out\n", adap->name); } @@ -576,7 +586,7 @@ int cec_thread_func(void *_adap) /* Tell the adapter to transmit, cancel on error */ if (adap->ops->adap_transmit(adap, data->attempts, signal_free_time, &data->msg)) - cec_data_cancel(data, CEC_TX_STATUS_ABORTED); + cec_data_cancel(data, CEC_TX_STATUS_ABORTED, 0); else adap->transmit_in_progress = true; @@ -738,9 +748,7 @@ static void cec_wait_timeout(struct work_struct *work) /* Mark the message as timed out */ list_del_init(&data->list); - data->msg.rx_ts = ktime_get_ns(); - data->msg.rx_status = CEC_RX_STATUS_TIMEOUT; - cec_data_completed(data); + cec_data_cancel(data, CEC_TX_STATUS_OK, CEC_RX_STATUS_TIMEOUT); unlock: mutex_unlock(&adap->lock); } @@ -926,8 +934,12 @@ int cec_transmit_msg_fh(struct cec_adapter *adap, struct cec_msg *msg, mutex_lock(&adap->lock); /* Cancel the transmit if it was interrupted */ - if (!data->completed) - cec_data_cancel(data, CEC_TX_STATUS_ABORTED); + if (!data->completed) { + if (data->msg.tx_status & CEC_TX_STATUS_OK) + cec_data_cancel(data, CEC_TX_STATUS_OK, CEC_RX_STATUS_ABORTED); + else + cec_data_cancel(data, CEC_TX_STATUS_ABORTED, 0); + } /* The transmit completed (possibly with an error) */ *msg = data->msg;