diff mbox

[PATCHv2,1/3] net: phy: prevent linking breakage

Message ID 1369741403-25315-2-git-send-email-alexandre.belloni@free-electrons.com (mailing list archive)
State New, archived
Headers show

Commit Message

Alexandre Belloni May 28, 2013, 11:43 a.m. UTC
phy_register_fixup{,_for_uid,_for_id} are called from arch/, quite
often, there is no protection to check whether CONFIG_PHYLIB=y which is
the only case where this would work. Having phylib as a module or not
compiled at all will result in that kind of linking failure:

arch/arm/mach-at91/built-in.o: In function `ksz9021rn_phy_fixup':
:(.text+0x1174): undefined reference to `mdiobus_write'
:(.text+0x1188): undefined reference to `mdiobus_write'
:(.text+0x119c): undefined reference to `mdiobus_write'
:(.text+0x11b0): undefined reference to `mdiobus_write'
arch/arm/mach-at91/built-in.o: In function `sama5_dt_device_init':
:(.init.text+0x1e34): undefined reference to `phy_register_fixup_for_uid'

Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>
---
 drivers/net/phy/phy_device.c |  6 ++++++
 include/linux/phy.h          | 33 ++++++++++++++++++++++++++++++---
 2 files changed, 36 insertions(+), 3 deletions(-)

Comments

David Miller May 28, 2013, 8:09 p.m. UTC | #1
From: Alexandre Belloni <alexandre.belloni@free-electrons.com>
Date: Tue, 28 May 2013 13:43:21 +0200

> phy_register_fixup{,_for_uid,_for_id} are called from arch/, quite
> often, there is no protection to check whether CONFIG_PHYLIB=y which is
> the only case where this would work. Having phylib as a module or not
> compiled at all will result in that kind of linking failure:
> 
> arch/arm/mach-at91/built-in.o: In function `ksz9021rn_phy_fixup':
> :(.text+0x1174): undefined reference to `mdiobus_write'
> :(.text+0x1188): undefined reference to `mdiobus_write'
> :(.text+0x119c): undefined reference to `mdiobus_write'
> :(.text+0x11b0): undefined reference to `mdiobus_write'
> arch/arm/mach-at91/built-in.o: In function `sama5_dt_device_init':
> :(.init.text+0x1e34): undefined reference to `phy_register_fixup_for_uid'
> 
> Signed-off-by: Alexandre Belloni <alexandre.belloni@free-electrons.com>

This is the wrong way to go about this.

If the arch code absolutely requires CONFIG_PHYLIB=y then express that
dependency in the arch Kconfig.  The arch code in question should not
be compiled at all if CONFIG_PHYLIB has an incompatible setting.
Alexandre Belloni May 29, 2013, 8:21 a.m. UTC | #2
Hi,

On 28/05/2013 22:09, David Miller wrote:
>
> This is the wrong way to go about this.
>
> If the arch code absolutely requires CONFIG_PHYLIB=y then express that
> dependency in the arch Kconfig.  The arch code in question should not
> be compiled at all if CONFIG_PHYLIB has an incompatible setting.
But that is making it impossible to compile a kernel without any network
stack for those platforms or we are going back to either enclosing the
calls to phy_register_fixup{,_for_uid,_for_id} with #ifdef CONFIG_PHYLIB
or if(IS_BUILTIN(CONFIG_PHYLIB)). And as you can see, it is quite error
prone and is done only done for 2 platforms on a total of 6. I believe
fixing that in phy.h is more foolproof.

Regards,
David Miller May 30, 2013, 9:42 a.m. UTC | #3
From: Alexandre Belloni <alexandre.belloni@free-electrons.com>
Date: Wed, 29 May 2013 10:21:26 +0200

> On 28/05/2013 22:09, David Miller wrote:
>>
>> This is the wrong way to go about this.
>>
>> If the arch code absolutely requires CONFIG_PHYLIB=y then express that
>> dependency in the arch Kconfig.  The arch code in question should not
>> be compiled at all if CONFIG_PHYLIB has an incompatible setting.
> But that is making it impossible to compile a kernel without any network
> stack for those platforms or we are going back to either enclosing the
> calls to phy_register_fixup{,_for_uid,_for_id} with #ifdef CONFIG_PHYLIB
> or if(IS_BUILTIN(CONFIG_PHYLIB)). And as you can see, it is quite error
> prone and is done only done for 2 platforms on a total of 6. I believe
> fixing that in phy.h is more foolproof.

Or you properly segregate the networking bits of the platform code so
that it can be built only when the necessary networking portions are
enabled.

Sometimes having dummy stubs makes sense, but not in this situation.
Arnd Bergmann June 4, 2013, 3:07 p.m. UTC | #4
On Thursday 30 May 2013 02:42:01 David Miller wrote:
> From: Alexandre Belloni <alexandre.belloni@free-electrons.com>
> > On 28/05/2013 22:09, David Miller wrote:
> > But that is making it impossible to compile a kernel without any network
> > stack for those platforms or we are going back to either enclosing the
> > calls to phy_register_fixup{,_for_uid,_for_id} with #ifdef CONFIG_PHYLIB
> > or if(IS_BUILTIN(CONFIG_PHYLIB)). And as you can see, it is quite error
> > prone and is done only done for 2 platforms on a total of 6. I believe
> > fixing that in phy.h is more foolproof.
> 
> Or you properly segregate the networking bits of the platform code so
> that it can be built only when the necessary networking portions are
> enabled.
> 
> Sometimes having dummy stubs makes sense, but not in this situation.

Currently most users of this function are doing something like

static int foo_phy_fixup(struct phy_device *phydev)
{
	...
}

static int __init boo_board_init(void)
{
	 if (IS_BUILTIN(CONFIG_PHYLIB))
		phy_register_fixup_for_uid(phy_id, foo_phy_fixup);
}

which is practically the same as having a dummy stub. It leads to
the foo_phy_fixup() function always getting compiled and then discarded
by gcc when CONFIG_PHYLIB is disabled.

The method is currently broken when network drivers are enabled as
modules, because we are missing the fixup then.

I think we should use IS_ENABLED() here to force a build error
in that case, and have something like

config ARCH_FOO
	bool "support for the foo platform"
	select PHYLIB if NET

in the platform Kconfig file, to ensure PHYLIB is always built-in.

I still think the inline alternatives would be helpful, but using
if (IS_ENABLED(CONFIG_PHYLIB)) in the platform code would also
work.

	Arnd
Florian Fainelli June 4, 2013, 3:36 p.m. UTC | #5
2013/6/4 Arnd Bergmann <arnd@arndb.de>:
>> Or you properly segregate the networking bits of the platform code so
>> that it can be built only when the necessary networking portions are
>> enabled.
>>
>> Sometimes having dummy stubs makes sense, but not in this situation.
>
> Currently most users of this function are doing something like
>
> static int foo_phy_fixup(struct phy_device *phydev)
> {
>         ...
> }
>
> static int __init boo_board_init(void)
> {
>          if (IS_BUILTIN(CONFIG_PHYLIB))
>                 phy_register_fixup_for_uid(phy_id, foo_phy_fixup);
> }
>
> which is practically the same as having a dummy stub. It leads to
> the foo_phy_fixup() function always getting compiled and then discarded
> by gcc when CONFIG_PHYLIB is disabled.
>
> The method is currently broken when network drivers are enabled as
> modules, because we are missing the fixup then.
>
> I think we should use IS_ENABLED() here to force a build error
> in that case, and have something like
>
> config ARCH_FOO
>         bool "support for the foo platform"
>         select PHYLIB if NET
>
> in the platform Kconfig file, to ensure PHYLIB is always built-in.
>
> I still think the inline alternatives would be helpful, but using
> if (IS_ENABLED(CONFIG_PHYLIB)) in the platform code would also
> work.

It seems to me that what David proposes is to have say an
arch/arm/mach-foo/phy-fixups.c file which is only enabled when
CONFIG_PHYLIB is set (obj-$(CONFIG_PHYLIB) += phy-fixup.o), such that
it does not need to have any conditionnals when calling
phy_register_fixup. This sounds a little unusual, but why not.
--
Florian
Arnd Bergmann June 4, 2013, 4:01 p.m. UTC | #6
On Tuesday 04 June 2013 16:36:50 Florian Fainelli wrote:
> It seems to me that what David proposes is to have say an
> arch/arm/mach-foo/phy-fixups.c file which is only enabled when
> CONFIG_PHYLIB is set (obj-$(CONFIG_PHYLIB) += phy-fixup.o), such that
> it does not need to have any conditionnals when calling
> phy_register_fixup. This sounds a little unusual, but why not.

I don't think it would actually help us, because then we still need
to declare a local function that gets called from the board init
code. Instead of doing

	if (IS_ENABLED(CONFIG_PHYLIB))
		 phy_register_fixup_for_uid(phy_id, foo_phy_fixup);

we would then do

	if (IS_ENABLED(CONFIG_PHYLIB))
		foo_phy_fixup_register();

which is not much different at all.

	Arnd
Florian Fainelli June 4, 2013, 4:09 p.m. UTC | #7
2013/6/4 Arnd Bergmann <arnd@arndb.de>:
> On Tuesday 04 June 2013 16:36:50 Florian Fainelli wrote:
>> It seems to me that what David proposes is to have say an
>> arch/arm/mach-foo/phy-fixups.c file which is only enabled when
>> CONFIG_PHYLIB is set (obj-$(CONFIG_PHYLIB) += phy-fixup.o), such that
>> it does not need to have any conditionnals when calling
>> phy_register_fixup. This sounds a little unusual, but why not.
>
> I don't think it would actually help us, because then we still need
> to declare a local function that gets called from the board init
> code. Instead of doing
>
>         if (IS_ENABLED(CONFIG_PHYLIB))
>                  phy_register_fixup_for_uid(phy_id, foo_phy_fixup);
>
> we would then do
>
>         if (IS_ENABLED(CONFIG_PHYLIB))
>                 foo_phy_fixup_register();
>
> which is not much different at all.

You would just need to define a stub for your arch_foo_phy_fixup()
which has a different definition depending on whether CONFIG_PHYLIB is
defined or not.

This would be just one function, instead of the whole bunch of stubs
needed for phylib. Right now its probably 1 vs 3, so it does not make
that much of a difference but who knows, if we had more phylib stubs
and forget to update the stubs? (which tends to happen pretty often).

The size savings are exactly the same in both approaches anyway.
--
Florian
Arnd Bergmann June 4, 2013, 5:17 p.m. UTC | #8
On Tuesday 04 June 2013 17:09:26 Florian Fainelli wrote:
> You would just need to define a stub for your arch_foo_phy_fixup()
> which has a different definition depending on whether CONFIG_PHYLIB is
> defined or not.

Yes, same thing. For a function that is called in only one place,
I would always prefer if(IS_ENABLED()) over a stub though.

> This would be just one function, instead of the whole bunch of stubs
> needed for phylib. Right now its probably 1 vs 3, so it does not make
> that much of a difference but who knows, if we had more phylib stubs
> and forget to update the stubs? (which tends to happen pretty often).
> 
> The size savings are exactly the same in both approaches anyway.

So should we just stick to the current method then and use
if (IS_ENABLED(CONFIG_NET)) for calling the function?

	Arnd
Alexandre Belloni June 5, 2013, 9:23 a.m. UTC | #9
On 04/06/2013 18:09, Florian Fainelli wrote:
> 2013/6/4 Arnd Bergmann <arnd@arndb.de>:
>> On Tuesday 04 June 2013 16:36:50 Florian Fainelli wrote:
>>> It seems to me that what David proposes is to have say an
>>> arch/arm/mach-foo/phy-fixups.c file which is only enabled when
>>> CONFIG_PHYLIB is set (obj-$(CONFIG_PHYLIB) += phy-fixup.o), such that
>>> it does not need to have any conditionnals when calling
>>> phy_register_fixup. This sounds a little unusual, but why not.
>> I don't think it would actually help us, because then we still need
>> to declare a local function that gets called from the board init
>> code. Instead of doing
>>
>>         if (IS_ENABLED(CONFIG_PHYLIB))
>>                  phy_register_fixup_for_uid(phy_id, foo_phy_fixup);
>>
>> we would then do
>>
>>         if (IS_ENABLED(CONFIG_PHYLIB))
>>                 foo_phy_fixup_register();
>>
>> which is not much different at all.
> You would just need to define a stub for your arch_foo_phy_fixup()
> which has a different definition depending on whether CONFIG_PHYLIB is
> defined or not.
>
> This would be just one function, instead of the whole bunch of stubs
> needed for phylib. Right now its probably 1 vs 3, so it does not make
> that much of a difference but who knows, if we had more phylib stubs
> and forget to update the stubs? (which tends to happen pretty often).

The fact is that for now it is called on 6 different platforms. Of those:
 - two (imx and mxs) are now doing it right *after* discovering the issue
 - two always selecting PHYLIB and can't be compiled without NET
 - two will break at compile time.

I'm really concerned about getting more platforms getting it wrong. I
think it boils down to who wants to take the maintenance burden. I would
believe that doing it in phylib is more future proof. But I'm fine
with whatever the consensus will be.

However, it is becoming urgent for Atmel to get the fix in 3.10 so that
they never get a kernel compilation that breaks.

Regards,
Arnd Bergmann June 5, 2013, 11:21 a.m. UTC | #10
On Wednesday 05 June 2013, Alexandre Belloni wrote:
> However, it is becoming urgent for Atmel to get the fix in 3.10 so that
> they never get a kernel compilation that breaks.

I don't think it's urgent since this is only about rare configurations,
and not a regression. We can queue the patch "ARM: at91: Fix link
breakage when !CONFIG_PHYLIB" you just sent to fix this, but I
would not consider it a serious problem to have it only in 3.11.

	Arnd
diff mbox

Patch

diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 3657b4a..df36367 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -64,6 +64,11 @@  static int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
 			     u32 flags, phy_interface_t interface);
 
 /*
+ *  phy_register_fixup{,_for_uid,_for_id} are called from arch/ so this won't
+ *  work unless phylib is compiled in the kernel.
+ */
+#ifdef CONFIG_PHYLIB
+/*
  * Creates a new phy_fixup and adds it to the list
  * @bus_id: A string which matches phydev->dev.bus_id (or PHY_ANY_ID)
  * @phy_uid: Used to match against phydev->phy_id (the UID of the PHY)
@@ -109,6 +114,7 @@  int phy_register_fixup_for_id(const char *bus_id,
 	return phy_register_fixup(bus_id, PHY_ANY_UID, 0xffffffff, run);
 }
 EXPORT_SYMBOL(phy_register_fixup_for_id);
+#endif /* CONFIG_PHYLIB */
 
 /*
  * Returns 1 if fixup matches phydev in bus_id and phy_uid.
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 9e11039..2cc3383 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -556,12 +556,39 @@  int phy_start_interrupts(struct phy_device *phydev);
 void phy_print_status(struct phy_device *phydev);
 void phy_device_free(struct phy_device *phydev);
 
+/*
+ *  phy_register_fixup{,_for_uid,_for_id} are called from arch/ so this won't
+ *  work unless phylib is compiled in the kernel.
+ *  Defining stubs allows to prevent linking errors.
+ */
+#ifdef CONFIG_PHYLIB
 int phy_register_fixup(const char *bus_id, u32 phy_uid, u32 phy_uid_mask,
-		int (*run)(struct phy_device *));
+		       int (*run)(struct phy_device *));
 int phy_register_fixup_for_id(const char *bus_id,
-		int (*run)(struct phy_device *));
+			      int (*run)(struct phy_device *));
 int phy_register_fixup_for_uid(u32 phy_uid, u32 phy_uid_mask,
-		int (*run)(struct phy_device *));
+			       int (*run)(struct phy_device *));
+#else
+static inline int phy_register_fixup(const char *bus_id, u32 phy_uid,
+				     u32 phy_uid_mask,
+				     int (*run)(struct phy_device *))
+{
+	return -ENOTSUPP;
+}
+
+static inline int phy_register_fixup_for_id(const char *bus_id,
+					    int (*run)(struct phy_device *))
+{
+	return -ENOTSUPP;
+}
+
+static inline int phy_register_fixup_for_uid(u32 phy_uid, u32 phy_uid_mask,
+					     int (*run)(struct phy_device *))
+{
+	return -ENOTSUPP;
+}
+#endif /* CONFIG_PHYLIB */
+
 int phy_scan_fixups(struct phy_device *phydev);
 
 int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable);