Re: [PATCH v3 17/17] i3c: renesas: Add runtime PM support

Claudiu Beznea <[email protected]>
Newsgroups org.infradead.lists.linux-i3c,dev.linux.lists.sashiko-reviews
Message-ID <[email protected]>

On 6/8/26 23:46, [email protected] wrote:
> Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
> 
> New issues:
> - [High] Async Runtime PM put during driver unbind leaves the clock enabled.
> - [High] Newly added renesas_i3c_abort_xfer clears the abort bit instead of setting it, leaving hardware stuck.
> 
> Pre-existing issues:
> - [High] Timeout handling in transfer loops fails to break the loop, ignores the timeout, and incorrectly returns success.
> - [High] I3C RX buffer is not advanced during FIFO reads, leaving buffer tails uninitialized.
> --
> 
> commit 810cc3873a9dc52a932c03376330719f06d17de0
> Author: Claudiu Beznea <[email protected]>
> 
> i3c: renesas: Add runtime PM support
> 
> This commit adds runtime PM support to the Renesas I3C driver, replacing
> explicit clock management with runtime PM APIs. It updates controller
> operations with PM guards and introduces a transfer abort mechanism to handle
> timeouts asynchronously.
> 
>> diff --git a/drivers/i3c/master/renesas-i3c.c b/drivers/i3c/master/renesas-i3c.c
>> index b6c282c53a541..d6121ca869908 100644
>> --- a/drivers/i3c/master/renesas-i3c.c
>> +++ b/drivers/i3c/master/renesas-i3c.c
> [ ... ]
>> @@ -443,7 +442,24 @@ static void renesas_i3c_enqueue_xfer(struct renesas_i3c *i3c, struct renesas_i3c
>>   	}
>>   }
>>   
>> -static void renesas_i3c_wait_xfer(struct renesas_i3c *i3c, struct renesas_i3c_xfer *xfer)
>> +static void renesas_i3c_abort_xfer(struct renesas_i3c *i3c)
>> +{
>> +	guard(spinlock_irqsave)(&i3c->xferqueue.lock);
>> +
>> +	/* Disable all the interrupts */
>> +	renesas_writel(i3c->regs, BIE, 0);
>> +	renesas_writel(i3c->regs, NTIE, 0);
>> +
>> +	/* Clear normal transfer status flags. */
>> +	renesas_clear_bit(i3c->regs, NTST, NTST_TDBEF0 | NTST_RDBFF0 | NTST_RSPQFF |
>> +					   NTST_TEF | NTST_TABTF);
>> +	/* Clear bus status flags. */
>> +	renesas_clear_bit(i3c->regs, BST, BST_NACKDF | BST_TENDF | BST_SPCNDDF);
>> +	/* Clear error flags. */
>> +	renesas_clear_bit(i3c->regs, BCTL, BCTL_ABT);
> 
> [Severity: High]
> Does this clear the abort bit instead of setting it?
> 
> Hardware abort bits like BCTL_ABT typically must be written as 1 to trigger
> an abort. If we only clear the bit, will the hardware controller be left
> running the timed-out transfer in the background and get stuck?

The role of the renesas_i3c_abort_xfer() is just to avoid triggering any new 
interrupt in case a transfer times out. The handler disables the interrupts and 
clear any status bits along with the BCTL.ABT bit.

Maybe a different name for the function would have been better.

> 
>> +}
>> +
> [ ... ]
>> @@ -975,9 +1027,14 @@ static int renesas_i3c_i3c_xfers(struct i3c_dev_desc *dev, struct i3c_xfer *i3c_
>>   				renesas_set_bit(i3c->regs, NTIE, NTIE_TDBEIE0);
>>   		}
>>   
>> -		renesas_i3c_wait_xfer(i3c, xfer);
>> +		time_left = renesas_i3c_wait_xfer(i3c, xfer);
>> +		if (!time_left)
>> +			abort_xfer = true;
> 
> [Severity: High]
> This isn't a bug introduced by this patch, but does the lack of a break here
> cause the loop to continue iterating?
> 
> If the transfer times out, it sets abort_xfer = true but continues to the
> next command, overwriting the shared cmd descriptor.
> 
> Also, since renesas_i3c_i3c_xfers() unconditionally returns 0 at the end,
> will this hide the timeout from the subsystem and potentially cause it to
> read uninitialized RX buffers?
> 
>>   	}
>>   
>> +	if (abort_xfer)
>> +		renesas_i3c_abort_xfer(i3c);
>> +
>>   	return 0;
>>   }
> [ ... ]
>> @@ -1045,8 +1112,12 @@ static void renesas_i3c_detach_i3c_dev(struct i3c_dev_desc *dev)
>>   	struct renesas_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev);
>>   	struct i3c_master_controller *m = i3c_dev_get_master(dev);
>>   	struct renesas_i3c *i3c = to_renesas_i3c(m);
>> +	int ret;
>>   
>> -	renesas_writel(i3c->regs, DATBAS(data->index), 0);
>> +	PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(i3c->dev, pm);
>> +	ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
>> +	if (!ret)
>> +		renesas_writel(i3c->regs, DATBAS(data->index), 0);
>>   
>>   	i3c_dev_set_master_data(dev, NULL);
>>   	i3c->addrs[data->index].addr = 0;
> 
> [Severity: High]
> Does this asynchronously put the runtime PM usage counter without a
> synchronous suspend before driver teardown completes?
> 
> During driver unbind, renesas_i3c_remove() unregisters the I3C master, which
> detaches devices. The PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND macro releases
> the device asynchronously via pm_runtime_put_autosuspend().
> 
> Right after remove returns, devres calls pm_runtime_disable(), which cancels
> the pending autosuspend work. 

Before calling pm_runtime_disable() the devres helper 
(pm_runtime_disable_action()) call:

pm_runtime_dont_use_autosuspend() ->
   __pm_runtime_use_autosuspend(dev, false) ->
     update_autosuspend(dev, old_delay, old_use) ->
        rpm_idle(dev, RPM_AUTO)

which should end up calling the idle and suspend callbacks.

> Could this leave the hardware permanently in
> the RPM_ACTIVE state and leak resources by never disabling the clocks?
> 
> [ ... ]
>> @@ -1102,7 +1180,9 @@ static int renesas_i3c_i2c_xfers(struct i2c_dev_desc *dev,
>>   
>>   		renesas_set_bit(i3c->regs, NTSTE, NTSTE_TDBEE0);
>>   
>> -		wait_for_completion_timeout(&xfer->comp, m->i2c.timeout);
>> +		time_left = wait_for_completion_timeout(&xfer->comp, m->i2c.timeout);
>> +		if (!time_left)
>> +			abort_xfer = true;
>>   
>>   		if (cmd->err)
>>   			break;
> 
> [Severity: High]
> This is a pre-existing issue, but similar to I3C transfers above, does this
> fail to break the loop when wait_for_completion_timeout() times out?
> 
> Since cmd->err remains 0 on timeout, the loop continues and eventually returns
> 0, hiding the timeout from the subsystem.
> 
> 
> Additionally, I noticed a pre-existing issue outside the immediate scope of
> this patch:
> 
> [Severity: High]
> This is a pre-existing issue, but in renesas_i3c_rx_isr() and
> renesas_i3c_resp_isr(), do the I3C FIFO reads copy data into cmd->rx_buf
> without advancing the buffer pointer?
> 
> renesas_i3c_rx_isr() does:
> 
>      i3c_readl_fifo(i3c->regs + NTDTBP0, cmd->rx_buf, read_bytes);
>      cmd->rx_count = read_bytes;
> 
> Since cmd->rx_buf is not incremented and rx_count is overwritten rather than
> accumulated, will multiple interrupts overwrite the beginning of the buffer?
> Could this leave the tail of the buffer uninitialized, leading to information
> leaks or data corruption when the subsystem reads it?
> 

-- 
Thank you,
Claudiu


-- 
linux-i3c mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/linux-i3c
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.