Writing Makefile code or How to do magic

Today I want to share with you my progress in Linux Kernel Backports project.

I recall that the task is to enable ‘make localmodconfig’ functionality for backported drivers.

The solution idea is the following:

  1. All devices and drivers have instances in virtual file system /sys. First step and the most challenging is to make this instances expose their Kconfig symbol (the CONFIG_FOO symbol that you need to select in the configuration phase in order to include that particular driver in the resulting kernel)
  2. Update systemd’s code to extract the Kconfig symbol from /sys  along with the other information.

With this implemented and having a mapping between the (kernel versions available modules (including backported ones) CONFIG_* symbols)  we could create a valid kernel configuration based on the udevadm output.

I have been working on the first step and I am proud to tell you that it is very close to its target. I will further present you the three approaches for this step:

Approach 1:

Idea: Every entry /sys is, from the kernel’s point of view, an instance of struct kobject kernel type. So, naturally, first approach was to somehow include this information into the kobject in order to be displyed in /sys.

So I did. Let’s have a look at the definition of struct kobject:

 63 struct kobject {
 64         const char              *name;
 65         struct list_head        entry;
 66         struct kobject          *parent;
 67         struct kset             *kset;
 68         struct kobj_type        *ktype;
 69         struct kernfs_node      *sd; /* sysfs directory entry */
 70         struct kref             kref;
 71 #ifdef CONFIG_DEBUG_KOBJECT_RELEASE
 72         struct delayed_work     release;
 73 #endif
 74         unsigned int state_initialized:1;
 75         unsigned int state_in_sysfs:1;
 76         unsigned int state_add_uevent_sent:1;
 77         unsigned int state_remove_uevent_sent:1;
 78         unsigned int uevent_suppress:1;
 79 };

 

I added another field kconfig_symbol to this structure dedicated to store the CONFIG_FOO symbol associated with the driver who owns the current kobject.

To clarify, let me give you a more pragmatic approach:

What exactly is a kobject ?   –  A directory in /sys

What exactly is a kobject attribute ? – A file in the directory

This is a very loose explanation. /sys is a virtual file system and, consequently, they are virtual directories and files, but from a user’s point of view the explanation is correct.

So, what I did is I added an extra attribute to this kobject named kobject_symbol.

As I said, this kobject_symbol appears as a file in /sys so I created an associate “show” function for it which returns the data from the kconfig_symbol field of kobject. This “show” function is called each time userspace trigger a read operation on this virtual file:

$ cat kconfig_symbol           —-     show() is called

CONFIG_FOO                          —-    content is displayed

Also, I updated the sysfs code in order to add this extra attribute along with the others when creating the /sys virtual file system at boot time.

All good so far. Since the population of the kconfig_symbol field was cumbersome, for test purposes,  I changed the show() function to return the constant string “CONFIG_GSOC”.

The result is that the kconfig_symbol attribute appears for each kobject but when trying to read the contents of this attribute nothing is displayed because the show() function is not called as it should.

Since this approach required modifying the code of the sysfs and potentially mess up the entire OS if the code varies for different OSes, we decided to take a more sandboxed approach.

Approach 2:

Since we are interested in  the CONFIG_* symbols for drivers, another idea is to add kconfig_symbol as a kernel struct module attribute. This structure is created for each driver required as module and not built-in so it’s conceptually closer to what we need.

This time there is a patch set because the implementation is more complex since I added also the generation and population of kconfig_symbol.

Step by step:

  1. Add kconfig_symbol as module field
  2. Add kconfig_symbol as module attribute using MODULE_ATTR macro which sets the default show() function [which displays the data from the field of struct module with the same name)
  3. Updated scripts/kconfig/streamline_config.pl to generate file scripts/Module.ksymb containing the associations (module-name – CONFIG_* symbol).
  4. Update modpost code to add populate this field in the auxiliary file generated at compilation [module-name.mod.c] with information provided by the file scripts/Module.ksymb

 

The full series of patches and the cover letter can be found here:

Cover letter: https://lkml.org/lkml/2016/7/31/84

Patches:

[RFC PATCH 1/3] Add kconfig_symbol attribute to struct module

[RFC PATCH 2/3] Add generation of Module.ksymb file in streamline_config.pl

[RFC PATCH 3/3] Add dynamic pegging of Kconfig symbol

The result of this patch set: attributes kconfig_symbol are added for most of the modules but not for all of them. This happens because modpost adds the attribute only if it finds suitable content for it in Module.ksymb [the associate CONFIG option]. But not for all modules a CONFIG symbol is encountered. Some of the module names do not match the makefile definition (the superobject in which are linked all other objects) and the internal definition (how is set in structure driver.name). A few examples:

Driver filename		Module name
-----------------------------------
lineage-pem[.o]		lineage_pem
phy-ab8500-usb[.o]	abx5x0-usb
ehci-mxc[.o]		mxc-ehci

Even though there are small differences between names, the current algorithm searches for exact matches so some modules are left out because of this.

Another problem would be that for some symbols have more associate CONFIG_* symbols in Module.ksymb. This happens due to more reasons:

  • duplicate checking it’s not done, so we end up having the same CONFIG_* symbol appear multiple times for one module name
  • for same module we have different CONFIG_* symbols for different architectures
  • in some cases we have multiple different CONFIG_* symbols for same object name because that object is linked within more modules; thus, all their CONFIGs appear in the list

Also, the mapping is inefficient since modpost searches for the CONFIG_* symbol for each module name.

Now introducing…… *DRUMS* ……

Approach 3

The idea here is that Makefiles from Linux Kernel tree contain all the information: module names and CONFIG_* symbols. So I tried to do the associations in-place in the Makefile hierarchy without external tools.

Before explaining what I did exactly I think I should clarify a bit how Makefiles work. You can understand a lot by reading files in Documentation/kconfig/ but let me highlight a few key points:

As you all know any LK compilation requires a .config configuration file which says which modules are compiled and how are they compiled (built-in or separated modules). So, supposing we have a .config file, when running make or make modules that file is included using the Makefile language directive include, thus is run like a regular Makefile. So what does the format

CONFIG_FOO_A=y

CONFIG_FOO_B=y

CONFIG_FOO_C=m

 mean if interpreted as a Makefile ?

Simple. It’s a series of variable assignments. After including the .config file all CONFIG_* symbols become Makefile variables with y/m as values. Further, when descending into subdirectories, Makefiles have the following format:

obj-$(CONFIG_FOO) = objfoo1.o objfoo2.o objfoo3.o

So, variable CONFIG_FOO is immediately resolved to y/m/n according to its previous setting and the variable obj-y or obj-m is set to a list of objects for the namespace of that directory.

Given this logic, associations CONFIG_* <-> module name are lost because of the make resolves the variables before I can intervene to save them. So, what I did:

  1. I kept the idea of using streamline_config.pl to generate Module.ksymb but this time I constrained it to generated only the objects which have exactly one CONFIG_* match. For selecting one module you need to set only one CONFIG_* symbol. So, logically, the objects which have exactly one CONFIG_* symbol are final modules and not objects useful at link time. This way I optimized also the size of Module.ksymb file and also I solved the “little-difference-in-names” problem.
  2. I kept the idea of adding an extra attribute to struct module named kconfig_symbol, displaying the CONFIG_* symbol associated with that module.
  3. I added dynamic pegging of CONFIG_* symbol: the Makefile adds at the compilation command the CONFIG_* symbol (deduced from the content of Module.ksymb) as a define KBUILD_KSYMB using the parameter -D
  4. I updated modpost to populate the kbuild_symbol attribute with the value of the KBUILD_KSYMB macro

This solution is extremely close to the target functionality. Tested on a standard system with Ubuntu it adds the kconfig_symbol attribute for all modules and populates it correctly for 54 out of 58 kernel modules. This is a very good rate. The few attributes that are not correctly populated belong to module names that, from various reasons, have more associated CONFIG_* options and are filtered by Module.ksymb generation.

However, approach number 3 is very close to what we need because it implements the infrastructure for loading of  associations (module name – CONFIG symbol) from Module.ksymb file and dynamic pegging of CONFIG symbol to the module during compilation. What is to be done now is to implement a better Module.ksymb generator. streamline_config.pl was a good choice at first but it was built for a different purpose and does not perfectly fit with the needs of this project.

This are the patches:

Cover letter: https://lkml.org/lkml/2016/8/17/471

Patches:

[RFC PATCH 1/5] Add generation of Module.symb in streamline_config

[RFC PATCH 2/5] Add CONFIG symbol to module as compilation parameter

[RFC PATCH 3/5] Trigger Module.ksymb generation in Makefile

[RFC PATCH 4/5] Set KCONFIG_KSYMB as value for kconfig_ksymb module attribute

[RFC PATCH 5/5] Add kconf_symb as kernel module attribute

Feel free to try them out 🙂

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s