Hi Dan - thanks for all your comments. Sorry it took a while to get to yours. On 17/09/2020 10:34, Dan Carpenter wrote: > On Wed, Sep 16, 2020 at 10:36:18PM +0100, Daniel Scally wrote: >> diff --git a/drivers/media/pci/intel/ipu3/ipu3-cio2.c b/drivers/media/pci/intel/ipu3/ipu3-cio2.c >> index 92f5eadf2c99..fd941d2c7581 100644 >> --- a/drivers/media/pci/intel/ipu3/ipu3-cio2.c >> +++ b/drivers/media/pci/intel/ipu3/ipu3-cio2.c >> @@ -1719,6 +1719,59 @@ static void cio2_queues_exit(struct cio2_device *cio2) >> cio2_queue_exit(cio2, &cio2->queue[i]); >> } >> >> +static int cio2_probe_can_progress(struct pci_dev *pci_dev) >> +{ >> + void *sensor; >> + >> + /* >> + * On ACPI platforms, we need to probe _after_ sensors wishing to connect >> + * to cio2 have added a device link. If there are no consumers yet, then >> + * we need to defer. The .sync_state() callback will then be called after >> + * all linked sensors have probed >> + */ >> + >> + if (IS_ENABLED(CONFIG_ACPI)) { > Reverse this condition. > > if (!IS_ENABLED(CONFIG_ACPI)) > return 0; > > Yeah, much better. >> + sensor = (struct device *)list_first_entry_or_null( >> + &pci_dev->dev.links.consumers, >> + struct dev_links_info, >> + consumers); >> + >> + if (!sensor) >> + return -EPROBE_DEFER; > Get rid of the cast. > > if (list_empty(&pci_dev->dev.links.consumers)) > return -EPROBE_DEFER; > > return 0; > Also much better, though I think possibly this whole section will be going away now after some of the other pointers... >> + cio2 = dev_get_drvdata(dev); >> + >> + if (!cio2) { > Delete the blank line between the call and the test. They're part of > the same step. "cio2" can't be NULL anyway, so delete the test. Thanks - I'll skip blank lines in that situation in future >> + >> + if (ret < 0) { >> + dev_err(dev, "Failed to fetch SSDB data\n"); >> + return ret; >> + } >> + >> + sensor->link = sensor_data.link; >> + sensor->lanes = sensor_data.lanes; >> + sensor->mclkspeed = sensor_data.mclkspeed; >> + >> + return 0; >> +} >> + >> +static int create_endpoint_properties(struct device *dev, >> + struct sensor_bios_data *ssdb, >> + struct property_entry *sensor_props, >> + struct property_entry *cio2_props) >> +{ >> + u32 *data_lanes; >> + int i; > Indented too far. > >> + >> + data_lanes = devm_kmalloc(dev, sizeof(u32) * (int)ssdb->lanes, > No need for the cast. Use devm_kmalloc_array(). Ah - TIL that that exists, thanks. >> + GFP_KERNEL); >> + >> + if (!data_lanes) { >> + dev_err(dev, >> + "Couldn't allocate memory for data lanes array\n"); > Delete the error message (checkpatch.pl --strict). And that too - I wasn't using the --strict flag, I'll do that next time >> + >> + sensor_props[0] = PROPERTY_ENTRY_U32("clock-frequency", >> + ssdb->mclkspeed); >> + sensor_props[1] = PROPERTY_ENTRY_U32("bus-type", 5); >> + sensor_props[2] = PROPERTY_ENTRY_U32("clock-lanes", 0); >> + sensor_props[3] = PROPERTY_ENTRY_U32_ARRAY_LEN("data-lanes", >> + data_lanes, >> + (int)ssdb->lanes); >> + sensor_props[4] = remote_endpoints[(bridge.n_sensors * 2) + ENDPOINT_SENSOR]; >> + sensor_props[5] = PROPERTY_ENTRY_NULL; >> + >> + cio2_props[0] = PROPERTY_ENTRY_U32_ARRAY_LEN("data-lanes", >> + data_lanes, >> + (int)ssdb->lanes); >> + cio2_props[1] = remote_endpoints[(bridge.n_sensors * 2) + ENDPOINT_CIO2]; >> + cio2_props[2] = PROPERTY_ENTRY_NULL; >> + >> + return 0; >> +} >> + >> +static int connect_supported_devices(void) >> +{ >> + struct acpi_device *adev; >> + struct device *dev; >> + struct sensor_bios_data ssdb; >> + struct sensor *sensor; >> + struct property_entry *sensor_props; >> + struct property_entry *cio2_props; >> + struct fwnode_handle *fwnode; >> + struct software_node *nodes; >> + struct v4l2_subdev *sd; >> + int i, ret; >> + >> + for (i = 0; i < ARRAY_SIZE(supported_devices); i++) { >> + adev = acpi_dev_get_first_match_dev(supported_devices[i], >> + NULL, -1); >> + >> + if (!adev) >> + continue; >> + >> + dev = bus_find_device_by_acpi_dev(&i2c_bus_type, adev); >> + >> + if (!dev) { >> + pr_info("ACPI match for %s, but it has no i2c device\n", >> + supported_devices[i]); >> + continue; >> + } >> + >> + if (!dev->driver_data) { >> + pr_info("ACPI match for %s, but it has no driver\n", >> + supported_devices[i]); > put_device(dev); Good catch, thank you. >> + } >> + >> + ret = connect_supported_devices(); >> + >> + if ((ret < 0) || (bridge.n_sensors <= 0)) { >> + pr_err("cio2_bridge: Failed to connect any devices\n"); >> + goto out; > If (bridge.n_sensors <= 0) is true then we need to set ret = -EINVAL > or something. Really .n_sensors can't be negative. > > The name "out" is a crappy label name because it doesn't say what the > goto does. When I scroll down then it turns out that "goto out;" calls > a free_everything() function. That kind of error handling is always > buggy. > > There are several typical bugs. 1) Something leaks because the error > handling style is too complicated to be audited. 2) Dereferencing > uninitialized pointers. 3) Undoing something which hasn't been done. > > I believe that in this case one bug is with the handling of the > bridge.cio2_fwnode. We "restore" it back to the original state > as soon as we have a non-NULL bridge.cio2 instead of waiting until we > have stored the original state. > > The best way to do error handling is this. > > Every function cleans up after itself. The connect_supported_devices() > function is a bit special because it's a loop. I would would write it > so that if it fails then it cleans up the partial loop iteration and > then at the end it cleans up all the failed loop iterations. > > for (i = 0; i < ARRAY_SIZE(supported_devices); i++) { > a = frob(); > if (!a) > goto unwind; > b = frob(); > if (!b) { > free(a); > goto unwind; > } > ... > } > > return 0; > > unwind: > for (i = 0; i < bridge.n_sensors; i++) { > free(b); > free(a); > } > bridge.n_sensors = 0; > > return ret; > > The problem with cio2_bridge_unregister_sensors() is that it doesn't > clean up partial iterations through the loop. (Missing calls to > put_device(dev)). > > Loops are complicated but the rest is simple. 1) Every allocation > function needs a matching cleanup function. 2) Use good label names > which say what the goto does. 3) The goto should free the most recent > successful allocation. > > a = frob(); > if (!a) > return -ENOMEM; > > b = frob(); > if (!b) { > ret = -ENOMEM; > goto free_a; > } > > c = frob(); > if (!c) { > ret = -ENOMEM; > goto free_b; > } > > return 0; > > free_b: > free(b); > free_a: > free(a); > > return ret; > > The free function doesn't have any if statements. > > void free_function() > { > free(c); > free(b); > free(a); > } > > The reviewer only needs to keep track of the most recent allocation > and verify that the goto free_foo matches what should be freed. This > system means the code is auditable (no leaks), you never free anything > which wasn't allocated. > This section and the other comments on error handling was really helpful - I appreciate you taking the time to explain so thoroughly.