diff mbox series

[net-next,1/6] net: mdio: add mdio_device_get() and mdio_device_put()

Message ID E1q2USm-008PAO-Gx@rmk-PC.armlinux.org.uk (mailing list archive)
State Accepted
Commit c4933fa88a68c69205753601044949d516c4db10
Delegated to: Netdev Maintainers
Headers show
Series net: pcs: add helpers to xpcs and lynx to manage mdiodev | expand

Checks

Context Check Description
netdev/series_format success Posting correctly formatted
netdev/tree_selection success Clearly marked for net-next
netdev/fixes_present success Fixes tag not required for -next series
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit success Errors and warnings before: 568 this patch: 568
netdev/cc_maintainers success CCed 4 of 4 maintainers
netdev/build_clang success Errors and warnings before: 295 this patch: 295
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/deprecated_api success None detected
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 538 this patch: 538
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 16 lines checked
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/source_inline success Was 0 now: 0

Commit Message

Russell King (Oracle) May 26, 2023, 10:14 a.m. UTC
Add two new operations for a mdio device to manage the refcount on the
underlying struct device. This will be used by mdio PCS drivers to
simplify the creation and destruction handling, making it easier for
users to get it correct.

Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
---
 include/linux/mdio.h | 10 ++++++++++
 1 file changed, 10 insertions(+)

Comments

Andy Shevchenko May 26, 2023, 6:20 p.m. UTC | #1
Fri, May 26, 2023 at 11:14:24AM +0100, Russell King (Oracle) kirjoitti:
> Add two new operations for a mdio device to manage the refcount on the
> underlying struct device. This will be used by mdio PCS drivers to
> simplify the creation and destruction handling, making it easier for
> users to get it correct.

...

> +static inline void mdio_device_get(struct mdio_device *mdiodev)
> +{
> +	get_device(&mdiodev->dev);

Dunno what is the practice in net, but it makes sense to have the pattern as

	if (mdiodev)
		return to_mdiodev(get_device(&mdiodev->dev));

which might be helpful in some cases. This approach is used in many cases in
the kernel.

> +}

P.S. Just my 2c, you may ignore the above comment.
Andrew Lunn May 29, 2023, 3:18 p.m. UTC | #2
On Fri, May 26, 2023 at 11:14:24AM +0100, Russell King (Oracle) wrote:
> Add two new operations for a mdio device to manage the refcount on the
> underlying struct device. This will be used by mdio PCS drivers to
> simplify the creation and destruction handling, making it easier for
> users to get it correct.
> 
> Signed-off-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>

Reviewed-by: Andrew Lunn <andrew@lunn.ch>

    Andrew
Andrew Lunn May 29, 2023, 3:21 p.m. UTC | #3
On Fri, May 26, 2023 at 09:20:17PM +0300, andy.shevchenko@gmail.com wrote:
> Fri, May 26, 2023 at 11:14:24AM +0100, Russell King (Oracle) kirjoitti:
> > Add two new operations for a mdio device to manage the refcount on the
> > underlying struct device. This will be used by mdio PCS drivers to
> > simplify the creation and destruction handling, making it easier for
> > users to get it correct.
> 
> ...
> 
> > +static inline void mdio_device_get(struct mdio_device *mdiodev)
> > +{
> > +	get_device(&mdiodev->dev);
> 
> Dunno what is the practice in net, but it makes sense to have the pattern as
> 
> 	if (mdiodev)
> 		return to_mdiodev(get_device(&mdiodev->dev));
> 
> which might be helpful in some cases. This approach is used in many cases in
> the kernel.

The device should exist. If it does not, it is a bug, and the
resulting opps will help find the bug.

	  Andrew
Andy Shevchenko May 29, 2023, 8:34 p.m. UTC | #4
On Mon, May 29, 2023 at 6:21 PM Andrew Lunn <andrew@lunn.ch> wrote:
> On Fri, May 26, 2023 at 09:20:17PM +0300, andy.shevchenko@gmail.com wrote:
> > Fri, May 26, 2023 at 11:14:24AM +0100, Russell King (Oracle) kirjoitti:

...

> > > +static inline void mdio_device_get(struct mdio_device *mdiodev)
> > > +{
> > > +   get_device(&mdiodev->dev);
> >
> > Dunno what is the practice in net, but it makes sense to have the pattern as
> >
> >       if (mdiodev)
> >               return to_mdiodev(get_device(&mdiodev->dev));
> >
> > which might be helpful in some cases. This approach is used in many cases in
> > the kernel.
>
> The device should exist. If it does not, it is a bug, and the
> resulting opps will help find the bug.

The main point in the returned value, the 'if' can be dropped, it's
not a big deal.
Russell King (Oracle) May 29, 2023, 10:41 p.m. UTC | #5
On Mon, May 29, 2023 at 11:34:42PM +0300, Andy Shevchenko wrote:
> On Mon, May 29, 2023 at 6:21 PM Andrew Lunn <andrew@lunn.ch> wrote:
> > On Fri, May 26, 2023 at 09:20:17PM +0300, andy.shevchenko@gmail.com wrote:
> > > Fri, May 26, 2023 at 11:14:24AM +0100, Russell King (Oracle) kirjoitti:
> 
> ...
> 
> > > > +static inline void mdio_device_get(struct mdio_device *mdiodev)
> > > > +{
> > > > +   get_device(&mdiodev->dev);
> > >
> > > Dunno what is the practice in net, but it makes sense to have the pattern as
> > >
> > >       if (mdiodev)
> > >               return to_mdiodev(get_device(&mdiodev->dev));
> > >
> > > which might be helpful in some cases. This approach is used in many cases in
> > > the kernel.
> >
> > The device should exist. If it does not, it is a bug, and the
> > resulting opps will help find the bug.
> 
> The main point in the returned value, the 'if' can be dropped, it's
> not a big deal.

And if you do, that results in a latent bug.

The whole point of doing the above is to cater for the case when
mdiodev is NULL. If mdiodev is NULL, provided "dev" is the first member
of mdiodev, then &mdiodev->dev will also be NULL. That will mean
get_device() will see a NULL pointer and itself return NULL. The
to_mdiodev() will then convert back to a mdiodev which will also be
NULL. So everything works correctly.

If dev is not the first member, then &mdiodev->dev will no longer be
NULL, but will be offset by that amount.

get_device() will check whether that is NULL - it won't be, so it'll
try to pass &dev->kobj to kobject_get(). That will also not be a NULL
pointer. kobject_get() will likely oops the kernel.

So no, it isn't safe to drop that if().

However, count the number of places in the kernel that actually pay
attention to the return value of get_device()...

In drivers/base, which should be the prime area where things are
done correctly, there are 42 places where get_device() is called.
Of those 42 places, 13 places make use of the returned value in
some way, which mean 29 do not bother to check.

Now, what use is checking that return value? Does get_device()
ever return anything different from what was passed in?

Well, we need to delve further down into kobject_get(), which
does not - kobject_get() returns whatever it was given. Note
that kref_get() doesn't return anything, nor does refcount_inc(),
both of which post-date get_device().

So, that in turn means that get_device() will only ever return
what it was passed. So:

	ret = get_device(dev);

`ret' is _guaranteed_ to always be exactly the same was `dev' in
all cases (except if "dev" results in get_device() performing an
invalid dereference and Oopsing the kernel, by which time we won't
care about `ret'.)

Now, if we think about situations where this will be called - they
will always be called where the MDIO device already has reference
counts against it - we have to be holding at least one refcount
to already have a reference to this device. If we don't have that,
then we're in the situation where the memory pointed to by the
mdio device pointer has probably already been freed, and could
already be used for some other purpose - and using the return value
from get_device() isn't going to save you from such a racy stupidity.

If we extend this to mdio_device_get(), then we end up in exactly
the same scenario - and what benefit does it give to the code?
Does it make the code more readable? No, it makes it less readable.
Does it make the code more robust? No, it makes no difference.
Does it make the code more correct? Again, no difference.

So, I don't see any point in changing the proposal I've put forward
as mdio_device_get().

Things would be different if get_device() used kobject_get_unless_zero()
which _can_ alter the returned value, but as I've stated above, if the
refcount were to become zero at the point that mdio_device_get() may
be called, we've *already* lost.
diff mbox series

Patch

diff --git a/include/linux/mdio.h b/include/linux/mdio.h
index 0670cc6e067c..c1b7008826e5 100644
--- a/include/linux/mdio.h
+++ b/include/linux/mdio.h
@@ -106,6 +106,16 @@  int mdio_driver_register(struct mdio_driver *drv);
 void mdio_driver_unregister(struct mdio_driver *drv);
 int mdio_device_bus_match(struct device *dev, struct device_driver *drv);
 
+static inline void mdio_device_get(struct mdio_device *mdiodev)
+{
+	get_device(&mdiodev->dev);
+}
+
+static inline void mdio_device_put(struct mdio_device *mdiodev)
+{
+	mdio_device_free(mdiodev);
+}
+
 static inline bool mdio_phy_id_is_c45(int phy_id)
 {
 	return (phy_id & MDIO_PHY_ID_C45) && !(phy_id & ~MDIO_PHY_ID_C45_MASK);