This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
---
i2c: mux: Propagate software nodes to channel adapters

This attaches the matching software-node child of the mux device to each
i2c-mux channel adapter, tracks whether the mux owns that attachment for
cleanup, and extends the I2C firmware-node adapter lookup so it also
matches a device's secondary fwnode.

> diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c
> index 3ec04787a7373..f39575ee02444 100644
> --- a/drivers/i2c/i2c-core-base.c
> +++ b/drivers/i2c/i2c-core-base.c
> @@ -1876,12 +1876,22 @@ int devm_i2c_add_adapter(struct device *dev, struct 
> i2c_adapter *adapter)
>  }
>  EXPORT_SYMBOL_GPL(devm_i2c_add_adapter);
>  
> -static int i2c_dev_or_parent_fwnode_match(struct device *dev, const void 
> *data)
> +static bool i2c_device_match_fwnode(struct device *dev, const void *data)
>  {
> +     struct fwnode_handle *fwnode = dev_fwnode(dev);
> +
>       if (device_match_fwnode(dev, data))
> +             return true;
> +
> +     return !IS_ERR_OR_NULL(fwnode) && fwnode->secondary == data;
> +}

[Severity: Medium]
Is the new fwnode->secondary dereference safe here?  The old code only used
device_match_fwnode(), which compares pointers and never dereferences the
fwnode.

i2c_dev_or_parent_fwnode_match() runs as a bus_find_device() match callback
from i2c_find_adapter_by_fwnode()/i2c_get_adapter_by_fwnode(), so it is
invoked for every device on i2c_bus_type, including clients, with no
device_lock held and nothing pinning dev->fwnode.

Meanwhile i2c_unregister_device() detaches and frees the software node
while the client is still in the bus klist:

drivers/i2c/i2c-core-base.c:i2c_unregister_device() {
        ...
        device_remove_software_node(&client->dev);
        device_unregister(&client->dev);
}

For a client created with info->swnode and no primary fwnode,
dev->fwnode == &swnode->fwnode, and device_remove_software_node() drops the
last reference:

drivers/base/swnode.c:device_remove_software_node() {
        ...
        set_secondary_fwnode(dev, NULL);
        swnode_put(swnode);
}

Can a lookup then observe fwnode = dev_fwnode(dev) before the free and read
fwnode->secondary after it?  The writers are unsynchronized too:

drivers/base/core.c:set_secondary_fwnode() {
        if (fwnode_is_primary(dev->fwnode))
                dev->fwnode->secondary = fwnode;
        else
                dev->fwnode = fwnode;
}

There is no lock common to the reader and the writer and no WRITE_ONCE, so
this also looks like a plain data race that could make the lookup return an
adapter whose software node has already been detached.

Also, does the extra dereference help in that case at all?  A software node
installed in the primary slot always has secondary == ERR_PTR(-ENODEV), so
it can never match through fwnode->secondary; only the window seems new.

> @@ -1891,8 +1901,9 @@ static int i2c_dev_or_parent_fwnode_match(struct device 
> *dev, const void *data)

[ ... ]

> diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c
> index 681a201c239ba..3bca123907b3d 100644
> --- a/drivers/i2c/i2c-mux.c
> +++ b/drivers/i2c/i2c-mux.c

[ ... ]

> @@ -364,6 +417,8 @@ int i2c_mux_add_adapter(struct i2c_mux_core *muxc,
>  
>               priv->adap.dev.of_node = child;
>               of_node_put(mux_node);
> +     } else {
> +             channel_node = i2c_mux_get_channel_swnode(muxc, chan_id);
>       }
>  
>       /*

[ ... ]

> @@ -408,6 +473,8 @@ int i2c_mux_add_adapter(struct i2c_mux_core *muxc,
>       return 0;
>  
>  err_free_priv:
> +     if (priv->swnode)
> +             device_remove_software_node(&priv->adap.dev);
>       kfree(priv);
>       return ret;
>  }

[Severity: Medium]
This isn't a bug introduced by this patch, but since err_free_priv is being
extended here, should it also drop the DT channel node reference?

In the DT branch of i2c_mux_add_adapter(), priv->adap.dev.of_node holds a
reference obtained either from the for_each_child_of_node() early break, or
from of_node_get(dev_node)/of_node_get(mux_node).  When
i2c_add_adapter()/i2c_add_numbered_adapter() fails, control reaches
err_free_priv, which only does kfree(priv).

That the mux owns that reference is visible in the success-path teardown,
which saves np = adap->dev.of_node and puts it after i2c_del_adapter():

> @@ -429,7 +496,13 @@ void i2c_mux_del_adapters(struct i2c_mux_core *muxc)
>               sysfs_remove_link(&priv->adap.dev.kobj, "mux_device");
> +             /*
> +              * Keep the software node through child removal. The adapter
> +              * device is cleared on deletion, so release the software-node
> +              * attachment reference via the saved handle afterwards.
> +              */
>               i2c_del_adapter(adap);
> +             fwnode_handle_put(priv->swnode);
>               of_node_put(np);
>               kfree(priv);
>       }

Neither i2c_register_adapter()'s error labels nor the driver core drops a
device's of_node reference when device_add() fails, so does the failure path
leak that struct device_node reference?  Would adding
of_node_put(priv->adap.dev.of_node) at err_free_priv be appropriate while
this label is being touched?

Reply via email to