[PATCH v4 06/16] i3c: renesas: Perform Dynamic Address Assignment on resume

Claudiu Beznea claudiu.beznea at kernel.org
Wed Jun 17 21:50:52 PDT 2026


Hi, I3C maintainers,

On 6/12/26 19:19, sashiko-bot at kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
> - [High] `renesas_i3c_group_devs_in_slots()` crashes due to NULL pointer dereference when swapping an I2C device into a previously empty slot.
> - [High] Slot state corruption and hardware configuration overwrite during device grouping.
> - [High] Build failure and state corruption due to calling undefined function `i3c_master_reattach_i3c_dev_locked()` and bypassing PID matching.
> --
> 
> commit e2b35e6a6eb1e4e88dd575a2f0b9fe6f7bf5f7c5
> Author: Claudiu Beznea <claudiu.beznea.uj at bp.renesas.com>
> 
> i3c: renesas: Perform Dynamic Address Assignment on resume
> 
> The Renesas RZ/G3S SoC supports a power saving mode where power to most
> SoC components, including I3C, is turned off. Running DAA in the controller
> resume path restores communication for devices that also lose power.
> 
>> diff --git a/drivers/i3c/master/renesas-i3c.c b/drivers/i3c/master/renesas-i3c.c
>> index 67b9a31300d09..a63edddbc9fe0 100644
>> --- a/drivers/i3c/master/renesas-i3c.c
>> +++ b/drivers/i3c/master/renesas-i3c.c
> 
> [ ... ]
> 
>> @@ -637,12 +646,80 @@ static void renesas_i3c_bus_cleanup(struct i3c_master_controller *m)
>>   	renesas_i3c_reset(i3c);
>>   }
>>   
>> +static u8 renesas_i3c_group_devs_in_slots(struct renesas_i3c *i3c)
>> +{
>> +	struct renesas_i3c_i2c_dev_data *new_i2c_data, *new_i3c_data;
>> +	struct renesas_i3c_addr i2c_addr, i3c_addr;
>> +	struct i3c_dev_desc *i3c_dev;
>> +	struct i2c_dev_desc *i2c_dev;
>> +	u8 i2c_pos = 0;
> 
> [ ... ]
> 
>> +	for (u8 pos = 0; pos < i3c->maxdevs; pos++) {
>> +		if (i3c->free_pos & BIT(pos))
>> +			continue;
>> +
>> +		if (!i3c->addrs[pos].is_i2c)
>> +			continue;
>> +
>> +		if (pos == i2c_pos) {
>> +			i2c_pos++;
>> +			continue;
>> +		}
>> +
>> +		/*
>> +		 * Swap the I3C device on i2c_pos slot with the I2C dev
>> +		 * on pos slot.
>> +		 */
>> +		i3c_addr = i3c->addrs[i2c_pos];
>> +		i3c_dev = i3c_addr.i3c_dev;
>> +
>> +		i2c_addr = i3c->addrs[pos];
>> +		i2c_dev = i2c_addr.i2c_dev;
>> +
>> +		new_i2c_data = i3c_dev_get_master_data(i3c_dev);
> 
> [Severity: High]
> Does this crash when swapping an I2C device into a previously empty slot?

I explained in [1] the rationale for which I chose it like this. I will adjust 
it accordingly.

[1] 
https://lore.kernel.org/sashiko-reviews/a538baca-fc5f-43db-8d19-2b5fa8c510dc@kernel.org/

> 
> When i2c_pos does not advance over empty slots, i3c_dev will be NULL if
> the destination slot is empty. Since i3c_dev_get_master_data() unconditionally
> dereferences the pointer (dev->common.master_priv), it seems this would cause
> a kernel panic when swapping into an empty slot.
> 
>> +		new_i3c_data = i2c_dev_get_master_data(i2c_dev);
>> +
>> +		i3c->addrs[i2c_pos] = i2c_addr;
>> +		i3c->addrs[i2c_pos].is_i2c = true;
>> +		i3c->addrs[i2c_pos].i2c_dev = i2c_dev;
>> +
>> +		i3c->addrs[pos] = i3c_addr;
>> +		i3c->addrs[pos].is_i2c = false;
>> +		i3c->addrs[pos].i3c_dev = i3c_dev;
>> +
>> +		new_i2c_data->index = i2c_pos++;
>> +		new_i3c_data->index = pos;
>> +
>> +		i2c_dev_set_master_data(i2c_dev, new_i2c_data);
>> +		i3c_dev_set_master_data(i3c_dev, new_i3c_data);
>> +	}
> 
> [ ... ]
> 
>> @@ -655,16 +732,25 @@ static int renesas_i3c_daa(struct i3c_master_controller *m)
>>   	olddevs = ~(i3c->free_pos);
>>   	i3c->internal_state = I3C_INTERNAL_STATE_CONTROLLER_ENTDAA;
>>   
>> +	if (i3c->resuming)
>> +		first_i3c_pos = renesas_i3c_group_devs_in_slots(i3c);
>> +
>>   	/* Setting DATBASn registers for target devices. */
>>   	for (pos = 0; pos < i3c->maxdevs; pos++) {
>> -		if (olddevs & BIT(pos))
>> -			continue;
>> +		if (olddevs & BIT(pos)) {
> 
> [Severity: High]
> Does this clobber the configuration of I2C devices swapped into empty slots?
> 
> Since renesas_i3c_group_devs_in_slots() does not update i3c->free_pos during
> a swap, the olddevs mask calculated before the grouping is now stale.

Will update the free_slots for this scenario as well.

> 
> If an I2C device was moved to a previously empty slot, olddevs & BIT(pos) will
> evaluate to false. This bypasses the skip logic, treats the newly moved I2C
> device as an unassigned slot, and generates a new dynamic address for it,
> blindly overwriting its DATBAS hardware configuration.
> 
> [ ... ]
> 
>> @@ -689,12 +780,38 @@ static int renesas_i3c_daa(struct i3c_master_controller *m)
>>   
>>   	renesas_i3c_wait_xfer(i3c, xfer);
>>   
>> -	newdevs = GENMASK(i3c->maxdevs - cmd->rx_count - 1, 0);
>> -	newdevs &= ~olddevs;
>> +	newdevs = GENMASK(i3c->maxdevs - cmd->rx_count - 1, first_i3c_pos);
>> +	/* Re-attach all the I3C devices on resume. */
>> +	if (!i3c->resuming)
>> +		newdevs &= ~olddevs;
>>   
>>   	for (pos = 0; pos < i3c->maxdevs; pos++) {
>> -		if (newdevs & BIT(pos))
>> -			i3c_master_add_i3c_dev_locked(m, i3c->addrs[pos]);
>> +		if (!(newdevs & BIT(pos)))
>> +			continue;
>> +
>> +		/*
>> +		 * If the bus was fully occupied before suspend, re-attach the
>> +		 * devices and update the addresses tracked by the subsystem.
>> +		 * Without this, i3c_master_add_i3c_dev_locked() returns errors
>> +		 * due to a lack of free slots (as reported by
>> +		 * renesas_i3c_get_free_pos()), causing the attachment to fail.
>> +		 *
>> +		 * Otherwise, follow the normal
>> +		 * i3c_master_add_i3c_dev_locked() path, which will re-attach the
>> +		 * devices, keep the subsystem state and driver addresses in sync,
>> +		 * and perform all the required internal bookkeeping.
>> +		 */
>> +		if (!i3c->free_pos && i3c->resuming) {
>> +			struct i3c_dev_desc *dev = i3c->addrs[pos].i3c_dev;
>> +			u8 old_dyn_addr;
>> +
>> +			old_dyn_addr = dev->info.dyn_addr;
>> +			dev->info.dyn_addr = i3c->addrs[pos].addr;
>> +
>> +			i3c_master_reattach_i3c_dev_locked(dev, old_dyn_addr);
> 
> [Severity: High]
> This calls i3c_master_reattach_i3c_dev_locked() which does not appear to exist
> in the kernel, causing a build failure.
> 
> Also, does this logic cross-wire physical devices with the wrong software
> structures?
> 
> It blindly assigns newly generated dynamic addresses to the pre-suspend
> device structures based solely on their slot index (pos). However, ENTDAA
> assigns addresses based on Provisioned ID (PID) arbitration order, not the
> previous slot indices. If the bus topology changes or devices arbitrate in a
> different order, commands might be sent to the incorrect targets, potentially
> causing data corruption.

I'm not sure how to solve this problem other than going through the 
i3c_master_add_i3c_dev_locked(). Or duplicate code. Maybe exporting 
i3c_master_detach_free_devs() and call it before running DAA to be able to run 
i3c_master_add_i3c_dev_locked() which takes care of all the attachment procedure?

I3C maintainers: could you please give some advice on this ?

This scenario is only for the case where the bus was fully occupied during a 
suspend/resume. Is it a valid case to have devices disappear during a 
suspend/resume?

Also, I want to mention that I currently don't have this problem, it was 
implemented as a result of it being pointed by sashiko.

-- 
Thank you,
Claudiu




More information about the linux-i3c mailing list