The __earlycon_table lives in a special "__earlycon_table" section. The
contents of this table are added using some macros that deposit individual
struct earlycon_id entries into this section. The linker then defines a symbol
__earlycon_table that is supposed to contain the addresss of the first of these
entries. The code in earlycon.c and fdt.c then tries to access the memory
pointed to by __earlycon_table as an array of struct earlycon_id entries.
Unfortunately, the compiler doesn't always place the entries such that they
are an array. Let's fix that.
Patch 1 of the series is a fix that should hopefully fix a kbuild error that
seems to be triggered by Patch 2.
Daniel Kurtz (2):
serial: sh-sci: Remove __initdata attribute for struct 'port_cfg'
earlycon: Use a pointer table to fix __earlycon_table stride
drivers/of/fdt.c | 7 +++++--
drivers/tty/serial/earlycon.c | 6 ++++--
drivers/tty/serial/sh-sci.c | 2 +-
include/asm-generic/vmlinux.lds.h | 2 +-
include/linux/serial_core.h | 21 ++++++++++++++-------
5 files changed, 25 insertions(+), 13 deletions(-)
--
2.16.2.804.g6dcf76e118-goog
Commit 99492c39f39f ("earlycon: Fix __earlycon_table stride") tried to fix
__earlycon_table stride by forcing the earlycon_id struct alignment to 32
and asking the linker to 32-byte align the __earlycon_table symbol. This
fix was based on commit 07fca0e57fca92 ("tracing: Properly align linker
defined symbols") which tried a similar fix for the tracing subsystem.
However, this fix doesn't quite work because there is no guarantee that
gcc will place structures packed into an array format. In fact, gcc 4.9
chooses to 64-byte align these structs by inserting additional padding
between the entries because it has no clue that they are supposed to be in
an array. If we are unlucky, the linker will assign symbol
"__earlycon_table" to a 32-byte aligned address which does not correpsond
to the 64-byte alignbed contents of section "__earlycon_table".
To address this same problem, the fix to the tracing system was
subsequently re-implemented using a more robust table of pointers approach
by commits:
3d56e331b653 ("tracing: Replace syscall_meta_data struct array with pointer array")
654986462939 ("tracepoints: Fix section alignment using pointer array")
e4a9ea5ee7c8 ("tracing: Replace trace_event struct array with pointer array")
Let's use this same "array of pointers to structs" approach for
EARLYCON_TABLE.
Fixes: 99492c39f39f ("earlycon: Fix __earlycon_table stride")
Signed-off-by: Daniel Kurtz <[email protected]>
Suggested-by: Aaron Durbin <[email protected]>
---
Changes since v1:
* added Suggested-by and Fixes, and reworded the commit message per Randy.
drivers/of/fdt.c | 7 +++++--
drivers/tty/serial/earlycon.c | 6 ++++--
include/asm-generic/vmlinux.lds.h | 2 +-
include/linux/serial_core.h | 21 ++++++++++++++-------
4 files changed, 24 insertions(+), 12 deletions(-)
diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
index 84aa9d676375..6da20b9688f7 100644
--- a/drivers/of/fdt.c
+++ b/drivers/of/fdt.c
@@ -942,7 +942,7 @@ int __init early_init_dt_scan_chosen_stdout(void)
int offset;
const char *p, *q, *options = NULL;
int l;
- const struct earlycon_id *match;
+ const struct earlycon_id **p_match;
const void *fdt = initial_boot_params;
offset = fdt_path_offset(fdt, "/chosen");
@@ -969,7 +969,10 @@ int __init early_init_dt_scan_chosen_stdout(void)
return 0;
}
- for (match = __earlycon_table; match < __earlycon_table_end; match++) {
+ for (p_match = __earlycon_table; p_match < __earlycon_table_end;
+ p_match++) {
+ const struct earlycon_id *match = *p_match;
+
if (!match->compatible[0])
continue;
diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c
index a24278380fec..22683393a0f2 100644
--- a/drivers/tty/serial/earlycon.c
+++ b/drivers/tty/serial/earlycon.c
@@ -169,7 +169,7 @@ static int __init register_earlycon(char *buf, const struct earlycon_id *match)
*/
int __init setup_earlycon(char *buf)
{
- const struct earlycon_id *match;
+ const struct earlycon_id **p_match;
if (!buf || !buf[0])
return -EINVAL;
@@ -177,7 +177,9 @@ int __init setup_earlycon(char *buf)
if (early_con.flags & CON_ENABLED)
return -EALREADY;
- for (match = __earlycon_table; match < __earlycon_table_end; match++) {
+ for (p_match = __earlycon_table; p_match < __earlycon_table_end;
+ p_match++) {
+ const struct earlycon_id *match = *p_match;
size_t len = strlen(match->name);
if (strncmp(buf, match->name, len))
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 1ab0e520d6fc..e17de55c2542 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -179,7 +179,7 @@
#endif
#ifdef CONFIG_SERIAL_EARLYCON
-#define EARLYCON_TABLE() STRUCT_ALIGN(); \
+#define EARLYCON_TABLE() . = ALIGN(8); \
VMLINUX_SYMBOL(__earlycon_table) = .; \
KEEP(*(__earlycon_table)) \
VMLINUX_SYMBOL(__earlycon_table_end) = .;
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index b32df49a3bd5..93b7add47087 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -351,10 +351,10 @@ struct earlycon_id {
char name[16];
char compatible[128];
int (*setup)(struct earlycon_device *, const char *options);
-} __aligned(32);
+};
-extern const struct earlycon_id __earlycon_table[];
-extern const struct earlycon_id __earlycon_table_end[];
+extern const struct earlycon_id *__earlycon_table[];
+extern const struct earlycon_id *__earlycon_table_end[];
#if defined(CONFIG_SERIAL_EARLYCON) && !defined(MODULE)
#define EARLYCON_USED_OR_UNUSED __used
@@ -362,12 +362,19 @@ extern const struct earlycon_id __earlycon_table_end[];
#define EARLYCON_USED_OR_UNUSED __maybe_unused
#endif
-#define OF_EARLYCON_DECLARE(_name, compat, fn) \
- static const struct earlycon_id __UNIQUE_ID(__earlycon_##_name) \
- EARLYCON_USED_OR_UNUSED __section(__earlycon_table) \
+#define _OF_EARLYCON_DECLARE(_name, compat, fn, unique_id) \
+ static const struct earlycon_id unique_id \
+ EARLYCON_USED_OR_UNUSED __initdata \
= { .name = __stringify(_name), \
.compatible = compat, \
- .setup = fn }
+ .setup = fn }; \
+ static const struct earlycon_id EARLYCON_USED_OR_UNUSED \
+ __section(__earlycon_table) \
+ * const __PASTE(__p, unique_id) = &unique_id
+
+#define OF_EARLYCON_DECLARE(_name, compat, fn) \
+ _OF_EARLYCON_DECLARE(_name, compat, fn, \
+ __UNIQUE_ID(__earlycon_##_name))
#define EARLYCON_DECLARE(_name, fn) OF_EARLYCON_DECLARE(_name, "", fn)
--
2.16.2.804.g6dcf76e118-goog
Commit dd076cffb8cd ("serial: sh-sci: Fix init data attribute for struct
'port_cfg'") properly removed the __init attribute, and changed it to
__initdata.
The __init function early_console_setup() takes the address of global
port_cfg and assigns it to a field in another global, sci_ports:
static int __init early_console_setup(struct earlycon_device *device,
int type)
{
...
port_cfg.type = type;
sci_ports[0].cfg = &port_cfg;
...
}
port_cfg, however, is now in __initdata:
static struct plat_sci_port port_cfg __initdata;
... but sci_ports is not:
static struct sci_port sci_ports[SCI_NPORTS];
Thus, there is a non-__initdata variable containing the address of a
__initdata struct.
Fix this section type conflict by just removing the __initdata attribute.
Fixes: dd076cffb8cd ("serial: sh-sci: Fix init data attribute for struct 'port_cfg'")
Signed-off-by: Daniel Kurtz <[email protected]>
---
drivers/tty/serial/sh-sci.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
index 44adf9db38f8..ff4e1012ed76 100644
--- a/drivers/tty/serial/sh-sci.c
+++ b/drivers/tty/serial/sh-sci.c
@@ -3277,7 +3277,7 @@ early_platform_init_buffer("earlyprintk", &sci_driver,
early_serial_buf, ARRAY_SIZE(early_serial_buf));
#endif
#ifdef CONFIG_SERIAL_SH_SCI_EARLYCON
-static struct plat_sci_port port_cfg __initdata;
+static struct plat_sci_port port_cfg;
static int __init early_console_setup(struct earlycon_device *device,
int type)
--
2.16.2.804.g6dcf76e118-goog
On Tue, Mar 20, 2018 at 10:57 AM, Daniel Kurtz <[email protected]> wrote:
> Commit 99492c39f39f ("earlycon: Fix __earlycon_table stride") tried to fix
> __earlycon_table stride by forcing the earlycon_id struct alignment to 32
> and asking the linker to 32-byte align the __earlycon_table symbol. This
> fix was based on commit 07fca0e57fca92 ("tracing: Properly align linker
> defined symbols") which tried a similar fix for the tracing subsystem.
>
> However, this fix doesn't quite work because there is no guarantee that
> gcc will place structures packed into an array format. In fact, gcc 4.9
> chooses to 64-byte align these structs by inserting additional padding
> between the entries because it has no clue that they are supposed to be in
> an array. If we are unlucky, the linker will assign symbol
> "__earlycon_table" to a 32-byte aligned address which does not correpsond
correspond
> to the 64-byte alignbed contents of section "__earlycon_table".
aligned
>
> To address this same problem, the fix to the tracing system was
> subsequently re-implemented using a more robust table of pointers approach
> by commits:
> 3d56e331b653 ("tracing: Replace syscall_meta_data struct array with pointer array")
> 654986462939 ("tracepoints: Fix section alignment using pointer array")
> e4a9ea5ee7c8 ("tracing: Replace trace_event struct array with pointer array")
>
> Let's use this same "array of pointers to structs" approach for
> EARLYCON_TABLE.
>
> Fixes: 99492c39f39f ("earlycon: Fix __earlycon_table stride")
> Signed-off-by: Daniel Kurtz <[email protected]>
> Suggested-by: Aaron Durbin <[email protected]>
> ---
> Changes since v1:
> * added Suggested-by and Fixes, and reworded the commit message per Randy.
>
> drivers/of/fdt.c | 7 +++++--
> drivers/tty/serial/earlycon.c | 6 ++++--
> include/asm-generic/vmlinux.lds.h | 2 +-
> include/linux/serial_core.h | 21 ++++++++++++++-------
> 4 files changed, 24 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
> index 84aa9d676375..6da20b9688f7 100644
> --- a/drivers/of/fdt.c
> +++ b/drivers/of/fdt.c
> @@ -942,7 +942,7 @@ int __init early_init_dt_scan_chosen_stdout(void)
> int offset;
> const char *p, *q, *options = NULL;
> int l;
> - const struct earlycon_id *match;
> + const struct earlycon_id **p_match;
> const void *fdt = initial_boot_params;
>
> offset = fdt_path_offset(fdt, "/chosen");
> @@ -969,7 +969,10 @@ int __init early_init_dt_scan_chosen_stdout(void)
> return 0;
> }
>
> - for (match = __earlycon_table; match < __earlycon_table_end; match++) {
> + for (p_match = __earlycon_table; p_match < __earlycon_table_end;
> + p_match++) {
> + const struct earlycon_id *match = *p_match;
> +
> if (!match->compatible[0])
> continue;
>
> diff --git a/drivers/tty/serial/earlycon.c b/drivers/tty/serial/earlycon.c
> index a24278380fec..22683393a0f2 100644
> --- a/drivers/tty/serial/earlycon.c
> +++ b/drivers/tty/serial/earlycon.c
> @@ -169,7 +169,7 @@ static int __init register_earlycon(char *buf, const struct earlycon_id *match)
> */
> int __init setup_earlycon(char *buf)
> {
> - const struct earlycon_id *match;
> + const struct earlycon_id **p_match;
>
> if (!buf || !buf[0])
> return -EINVAL;
> @@ -177,7 +177,9 @@ int __init setup_earlycon(char *buf)
> if (early_con.flags & CON_ENABLED)
> return -EALREADY;
>
> - for (match = __earlycon_table; match < __earlycon_table_end; match++) {
> + for (p_match = __earlycon_table; p_match < __earlycon_table_end;
> + p_match++) {
> + const struct earlycon_id *match = *p_match;
> size_t len = strlen(match->name);
>
> if (strncmp(buf, match->name, len))
> diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
> index 1ab0e520d6fc..e17de55c2542 100644
> --- a/include/asm-generic/vmlinux.lds.h
> +++ b/include/asm-generic/vmlinux.lds.h
> @@ -179,7 +179,7 @@
> #endif
>
> #ifdef CONFIG_SERIAL_EARLYCON
> -#define EARLYCON_TABLE() STRUCT_ALIGN(); \
> +#define EARLYCON_TABLE() . = ALIGN(8); \
> VMLINUX_SYMBOL(__earlycon_table) = .; \
> KEEP(*(__earlycon_table)) \
> VMLINUX_SYMBOL(__earlycon_table_end) = .;
> diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
> index b32df49a3bd5..93b7add47087 100644
> --- a/include/linux/serial_core.h
> +++ b/include/linux/serial_core.h
> @@ -351,10 +351,10 @@ struct earlycon_id {
> char name[16];
> char compatible[128];
> int (*setup)(struct earlycon_device *, const char *options);
> -} __aligned(32);
> +};
>
> -extern const struct earlycon_id __earlycon_table[];
> -extern const struct earlycon_id __earlycon_table_end[];
> +extern const struct earlycon_id *__earlycon_table[];
> +extern const struct earlycon_id *__earlycon_table_end[];
>
> #if defined(CONFIG_SERIAL_EARLYCON) && !defined(MODULE)
> #define EARLYCON_USED_OR_UNUSED __used
> @@ -362,12 +362,19 @@ extern const struct earlycon_id __earlycon_table_end[];
> #define EARLYCON_USED_OR_UNUSED __maybe_unused
> #endif
>
> -#define OF_EARLYCON_DECLARE(_name, compat, fn) \
> - static const struct earlycon_id __UNIQUE_ID(__earlycon_##_name) \
> - EARLYCON_USED_OR_UNUSED __section(__earlycon_table) \
> +#define _OF_EARLYCON_DECLARE(_name, compat, fn, unique_id) \
> + static const struct earlycon_id unique_id \
> + EARLYCON_USED_OR_UNUSED __initdata \
> = { .name = __stringify(_name), \
> .compatible = compat, \
> - .setup = fn }
> + .setup = fn }; \
> + static const struct earlycon_id EARLYCON_USED_OR_UNUSED \
> + __section(__earlycon_table) \
> + * const __PASTE(__p, unique_id) = &unique_id
> +
> +#define OF_EARLYCON_DECLARE(_name, compat, fn) \
> + _OF_EARLYCON_DECLARE(_name, compat, fn, \
> + __UNIQUE_ID(__earlycon_##_name))
>
> #define EARLYCON_DECLARE(_name, fn) OF_EARLYCON_DECLARE(_name, "", fn)
>
> --
> 2.16.2.804.g6dcf76e118-goog
>
Hi Daniel,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on tty/tty-testing]
[also build test ERROR on v4.16-rc6 next-20180322]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Daniel-Kurtz/serial-sh-sci-Remove-__initdata-attribute-for-struct-port_cfg/20180323-085004
base: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git tty-testing
config: i386-allyesconfig (attached as .config)
compiler: gcc-7 (Debian 7.3.0-1) 7.3.0
reproduce:
# save the attached .config to linux build tree
make ARCH=i386
All errors (new ones prefixed by >>):
>> drivers/tty/serial/sh-sci.c:3293:81: error: early_driver causes a section type conflict with __UNIQUE_ID___earlycon_hscif44
early_platform_init_buffer("earlyprintk", &sci_driver,
^
drivers/tty/serial/sh-sci.c:3349:33: note: '__UNIQUE_ID___earlycon_hscif44' was declared here
OF_EARLYCON_DECLARE(hscif, "renesas,hscif", hscif_early_console_setup);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
vim +3293 drivers/tty/serial/sh-sci.c
^1da177e drivers/serial/sh-sci.c Linus Torvalds 2005-04-16 3291
7b6fd3bf drivers/serial/sh-sci.c Magnus Damm 2009-12-14 3292 #ifdef CONFIG_SERIAL_SH_SCI_CONSOLE
7b6fd3bf drivers/serial/sh-sci.c Magnus Damm 2009-12-14 @3293 early_platform_init_buffer("earlyprintk", &sci_driver,
7b6fd3bf drivers/serial/sh-sci.c Magnus Damm 2009-12-14 3294 early_serial_buf, ARRAY_SIZE(early_serial_buf));
7b6fd3bf drivers/serial/sh-sci.c Magnus Damm 2009-12-14 3295 #endif
0b0cced1 drivers/tty/serial/sh-sci.c Yoshinori Sato 2015-12-24 3296 #ifdef CONFIG_SERIAL_SH_SCI_EARLYCON
2897809d drivers/tty/serial/sh-sci.c Daniel Kurtz 2018-03-20 3297 static struct plat_sci_port port_cfg;
0b0cced1 drivers/tty/serial/sh-sci.c Yoshinori Sato 2015-12-24 3298
:::::: The code at line 3293 was first introduced by commit
:::::: 7b6fd3bf82c4901f6ba0101ba71a5c507c24f9cf sh-sci: Extend sh-sci driver with early console V2
:::::: TO: Magnus Damm <[email protected]>
:::::: CC: Paul Mundt <[email protected]>
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
On Tue, Mar 20, 2018 at 11:57:10AM -0600, Daniel Kurtz wrote:
> The __earlycon_table lives in a special "__earlycon_table" section. The
> contents of this table are added using some macros that deposit individual
> struct earlycon_id entries into this section. The linker then defines a symbol
> __earlycon_table that is supposed to contain the addresss of the first of these
> entries. The code in earlycon.c and fdt.c then tries to access the memory
> pointed to by __earlycon_table as an array of struct earlycon_id entries.
>
> Unfortunately, the compiler doesn't always place the entries such that they
> are an array. Let's fix that.
>
> Patch 1 of the series is a fix that should hopefully fix a kbuild error that
> seems to be triggered by Patch 2.
Doesn't seem like that worked :(
And I have a bunch of different versions of this patch in my to-review
queue, and I can't figure out which is the "latest" one.
Can you resend it, after getting the build error fixed, so I know which
to attempt to apply?
thanks,
greg k-h
On Fri, Mar 23, 2018 at 7:34 AM Greg Kroah-Hartman <
[email protected]> wrote:
> On Tue, Mar 20, 2018 at 11:57:10AM -0600, Daniel Kurtz wrote:
> > The __earlycon_table lives in a special "__earlycon_table" section. The
> > contents of this table are added using some macros that deposit
individual
> > struct earlycon_id entries into this section. The linker then defines
a symbol
> > __earlycon_table that is supposed to contain the addresss of the first
of these
> > entries. The code in earlycon.c and fdt.c then tries to access the
memory
> > pointed to by __earlycon_table as an array of struct earlycon_id
entries.
> >
> > Unfortunately, the compiler doesn't always place the entries such that
they
> > are an array. Let's fix that.
> >
> > Patch 1 of the series is a fix that should hopefully fix a kbuild error
that
> > seems to be triggered by Patch 2.
> Doesn't seem like that worked :(
> And I have a bunch of different versions of this patch in my to-review
> queue, and I can't figure out which is the "latest" one.
> Can you resend it, after getting the build error fixed, so I know which
> to attempt to apply?
Please take Patch Set v5 (fixes kbuild errors and commit message typos):
https://patchwork.kernel.org/patch/10298043/
thanks,
-djk
> thanks,
> greg k-h