[v4,09/14] dmaengine: tegra-apb: Clean up runtime PM teardown
diff mbox series

Message ID 20200112173006.29863-10-digetx@gmail.com
State Changes Requested
Headers show
Series
  • NVIDIA Tegra APB DMA driver fixes and improvements
Related show

Commit Message

Dmitry Osipenko Jan. 12, 2020, 5:30 p.m. UTC
It's cleaner to teardown RPM by revering the enable sequence, which makes
code much easier to follow.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
 drivers/dma/tegra20-apb-dma.c | 22 +++++++++++++---------
 1 file changed, 13 insertions(+), 9 deletions(-)

Comments

Jon Hunter Jan. 15, 2020, 9:57 a.m. UTC | #1
On 12/01/2020 17:30, Dmitry Osipenko wrote:
> It's cleaner to teardown RPM by revering the enable sequence, which makes
> code much easier to follow.
> 
> Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
> ---
>  drivers/dma/tegra20-apb-dma.c | 22 +++++++++++++---------
>  1 file changed, 13 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c
> index 7158bd3145c4..cc4a9ca20780 100644
> --- a/drivers/dma/tegra20-apb-dma.c
> +++ b/drivers/dma/tegra20-apb-dma.c
> @@ -1429,13 +1429,15 @@ static int tegra_dma_probe(struct platform_device *pdev)
>  	spin_lock_init(&tdma->global_lock);
>  
>  	pm_runtime_enable(&pdev->dev);
> -	if (!pm_runtime_enabled(&pdev->dev))
> +	if (!pm_runtime_enabled(&pdev->dev)) {
>  		ret = tegra_dma_runtime_resume(&pdev->dev);
> -	else
> +		if (ret)
> +			return ret;
> +	} else {
>  		ret = pm_runtime_get_sync(&pdev->dev);
> -
> -	if (ret < 0)
> -		goto err_pm_disable;
> +		if (ret < 0)
> +			goto err_pm_disable;
> +	}
>  
>  	/* Reset DMA controller */
>  	reset_control_assert(tdma->rst);
> @@ -1545,9 +1547,10 @@ static int tegra_dma_probe(struct platform_device *pdev)
>  	dma_async_device_unregister(&tdma->dma_dev);
>  
>  err_pm_disable:
> -	pm_runtime_disable(&pdev->dev);
> -	if (!pm_runtime_status_suspended(&pdev->dev))
> +	if (!pm_runtime_enabled(&pdev->dev))
>  		tegra_dma_runtime_suspend(&pdev->dev);
> +	else
> +		pm_runtime_disable(&pdev->dev);
>  
>  	return ret;
>  }
> @@ -1558,9 +1561,10 @@ static int tegra_dma_remove(struct platform_device *pdev)
>  
>  	dma_async_device_unregister(&tdma->dma_dev);
>  
> -	pm_runtime_disable(&pdev->dev);
> -	if (!pm_runtime_status_suspended(&pdev->dev))
> +	if (!pm_runtime_enabled(&pdev->dev))
>  		tegra_dma_runtime_suspend(&pdev->dev);
> +	else
> +		pm_runtime_disable(&pdev->dev);

Looks like dma_async_device_unregister() will warn if a client still has
a channel requested but does not prevent the unregister from completing.
So it could be possible that we could be leaving the controller active now.

Jon
Dmitry Osipenko Jan. 16, 2020, 5:18 p.m. UTC | #2
15.01.2020 12:57, Jon Hunter пишет:
> 
> On 12/01/2020 17:30, Dmitry Osipenko wrote:
>> It's cleaner to teardown RPM by revering the enable sequence, which makes
>> code much easier to follow.
>>
>> Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
>> ---
>>  drivers/dma/tegra20-apb-dma.c | 22 +++++++++++++---------
>>  1 file changed, 13 insertions(+), 9 deletions(-)
>>
>> diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c
>> index 7158bd3145c4..cc4a9ca20780 100644
>> --- a/drivers/dma/tegra20-apb-dma.c
>> +++ b/drivers/dma/tegra20-apb-dma.c
>> @@ -1429,13 +1429,15 @@ static int tegra_dma_probe(struct platform_device *pdev)
>>  	spin_lock_init(&tdma->global_lock);
>>  
>>  	pm_runtime_enable(&pdev->dev);
>> -	if (!pm_runtime_enabled(&pdev->dev))
>> +	if (!pm_runtime_enabled(&pdev->dev)) {
>>  		ret = tegra_dma_runtime_resume(&pdev->dev);
>> -	else
>> +		if (ret)
>> +			return ret;
>> +	} else {
>>  		ret = pm_runtime_get_sync(&pdev->dev);
>> -
>> -	if (ret < 0)
>> -		goto err_pm_disable;
>> +		if (ret < 0)
>> +			goto err_pm_disable;
>> +	}
>>  
>>  	/* Reset DMA controller */
>>  	reset_control_assert(tdma->rst);
>> @@ -1545,9 +1547,10 @@ static int tegra_dma_probe(struct platform_device *pdev)
>>  	dma_async_device_unregister(&tdma->dma_dev);
>>  
>>  err_pm_disable:
>> -	pm_runtime_disable(&pdev->dev);
>> -	if (!pm_runtime_status_suspended(&pdev->dev))
>> +	if (!pm_runtime_enabled(&pdev->dev))
>>  		tegra_dma_runtime_suspend(&pdev->dev);
>> +	else
>> +		pm_runtime_disable(&pdev->dev);
>>  
>>  	return ret;
>>  }
>> @@ -1558,9 +1561,10 @@ static int tegra_dma_remove(struct platform_device *pdev)
>>  
>>  	dma_async_device_unregister(&tdma->dma_dev);
>>  
>> -	pm_runtime_disable(&pdev->dev);
>> -	if (!pm_runtime_status_suspended(&pdev->dev))
>> +	if (!pm_runtime_enabled(&pdev->dev))
>>  		tegra_dma_runtime_suspend(&pdev->dev);
>> +	else
>> +		pm_runtime_disable(&pdev->dev);
> 
> Looks like dma_async_device_unregister() will warn if a client still has
> a channel requested but does not prevent the unregister from completing.
> So it could be possible that we could be leaving the controller active now.

It's a drivers dependency bug if DMA driver's module isn't properly
refcounted and thus could be removed while it has active users. Nothing
we can do about it here, the actual source of the bug needs to be fixed.

Perhaps Tegra DMA driver could inc/dec module's refcounf on channel's
request/free, but I think it should be responsibility of the DMA core to
care about the refcounting (if it doesn't do it already).

Patch
diff mbox series

diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c
index 7158bd3145c4..cc4a9ca20780 100644
--- a/drivers/dma/tegra20-apb-dma.c
+++ b/drivers/dma/tegra20-apb-dma.c
@@ -1429,13 +1429,15 @@  static int tegra_dma_probe(struct platform_device *pdev)
 	spin_lock_init(&tdma->global_lock);
 
 	pm_runtime_enable(&pdev->dev);
-	if (!pm_runtime_enabled(&pdev->dev))
+	if (!pm_runtime_enabled(&pdev->dev)) {
 		ret = tegra_dma_runtime_resume(&pdev->dev);
-	else
+		if (ret)
+			return ret;
+	} else {
 		ret = pm_runtime_get_sync(&pdev->dev);
-
-	if (ret < 0)
-		goto err_pm_disable;
+		if (ret < 0)
+			goto err_pm_disable;
+	}
 
 	/* Reset DMA controller */
 	reset_control_assert(tdma->rst);
@@ -1545,9 +1547,10 @@  static int tegra_dma_probe(struct platform_device *pdev)
 	dma_async_device_unregister(&tdma->dma_dev);
 
 err_pm_disable:
-	pm_runtime_disable(&pdev->dev);
-	if (!pm_runtime_status_suspended(&pdev->dev))
+	if (!pm_runtime_enabled(&pdev->dev))
 		tegra_dma_runtime_suspend(&pdev->dev);
+	else
+		pm_runtime_disable(&pdev->dev);
 
 	return ret;
 }
@@ -1558,9 +1561,10 @@  static int tegra_dma_remove(struct platform_device *pdev)
 
 	dma_async_device_unregister(&tdma->dma_dev);
 
-	pm_runtime_disable(&pdev->dev);
-	if (!pm_runtime_status_suspended(&pdev->dev))
+	if (!pm_runtime_enabled(&pdev->dev))
 		tegra_dma_runtime_suspend(&pdev->dev);
+	else
+		pm_runtime_disable(&pdev->dev);
 
 	return 0;
 }