Skip to content

Commit

Permalink
net: dsa: fix of_mdio_find_bus() device refcount leak
Browse files Browse the repository at this point in the history
Current users of of_mdio_find_bus() leak a struct device refcount, as
they fail to clean up the reference obtained inside class_find_device().

Fix the DSA code to properly refcount the returned MDIO bus by:
1. taking a reference on the struct device whenever we assign it to
   pd->chip[x].host_dev.
2. dropping the reference when we overwrite the existing reference.
3. dropping the reference when we free the data structure.
4. dropping the initial reference we obtained after setting up the
   platform data structure, or on failure.

In step 2 above, where we obtain a new MDIO bus, there is no need to
take a reference on it as we would only have to drop it immediately
after assignment again, iow:

	put_device(cd->host_dev);	/* drop original assignment ref */
	cd->host_dev = get_device(&mdio_bus_switch->dev); /* get our ref */
	put_device(&mdio_bus_switch->dev); /* drop of_mdio_find_bus ref */

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Russell King authored and David S. Miller committed Sep 25, 2015
1 parent a136442 commit e496ae6
Showing 1 changed file with 31 additions and 7 deletions.
38 changes: 31 additions & 7 deletions net/dsa/dsa.c
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,10 @@ static void dsa_of_free_platform_data(struct dsa_platform_data *pd)
port_index++;
}
kfree(pd->chip[i].rtable);

/* Drop our reference to the MDIO bus device */
if (pd->chip[i].host_dev)
put_device(pd->chip[i].host_dev);
}
kfree(pd->chip);
}
Expand Down Expand Up @@ -661,16 +665,22 @@ static int dsa_of_probe(struct device *dev)
return -EPROBE_DEFER;

ethernet = of_parse_phandle(np, "dsa,ethernet", 0);
if (!ethernet)
return -EINVAL;
if (!ethernet) {
ret = -EINVAL;
goto out_put_mdio;
}

ethernet_dev = of_find_net_device_by_node(ethernet);
if (!ethernet_dev)
return -EPROBE_DEFER;
if (!ethernet_dev) {
ret = -EPROBE_DEFER;
goto out_put_mdio;
}

pd = kzalloc(sizeof(*pd), GFP_KERNEL);
if (!pd)
return -ENOMEM;
if (!pd) {
ret = -ENOMEM;
goto out_put_mdio;
}

dev->platform_data = pd;
pd->of_netdev = ethernet_dev;
Expand All @@ -691,7 +701,9 @@ static int dsa_of_probe(struct device *dev)
cd = &pd->chip[chip_index];

cd->of_node = child;
cd->host_dev = &mdio_bus->dev;

/* When assigning the host device, increment its refcount */
cd->host_dev = get_device(&mdio_bus->dev);

sw_addr = of_get_property(child, "reg", NULL);
if (!sw_addr)
Expand All @@ -711,6 +723,12 @@ static int dsa_of_probe(struct device *dev)
ret = -EPROBE_DEFER;
goto out_free_chip;
}

/* Drop the mdio_bus device ref, replacing the host
* device with the mdio_bus_switch device, keeping
* the refcount from of_mdio_find_bus() above.
*/
put_device(cd->host_dev);
cd->host_dev = &mdio_bus_switch->dev;
}

Expand Down Expand Up @@ -744,13 +762,19 @@ static int dsa_of_probe(struct device *dev)
}
}

/* The individual chips hold their own refcount on the mdio bus,
* so drop ours */
put_device(&mdio_bus->dev);

return 0;

out_free_chip:
dsa_of_free_platform_data(pd);
out_free:
kfree(pd);
dev->platform_data = NULL;
out_put_mdio:
put_device(&mdio_bus->dev);
return ret;
}

Expand Down

0 comments on commit e496ae6

Please sign in to comment.