Understanding the container_of Macro in Linux kernel

This article is the continuation of the Series on the C programming tutorial and carries the discussion on C language programming and its implementation. It aims to provide easy and practical examples for understanding the C program. In our last article, we have seen the Stringizing and Token Pasting Operators in C programming.

The Linux kernel contains a very useful macro named container_of. This article is about understanding the container_of macro in Linux kernel. This article includes a simple program that illustrates how this macro is used and explains why it is so useful.

You can also read flexible array members in c, macro vs inline, and embedded interview topics.

Understanding the container_of Macro in Linux kernel

Introduction

Now we will assume that we have three containers. Container-1 has gold, Container-2 has diamond and Container-3 has filled with water (Probably water is the most precious than gold and diamond). One guy comes and asks you which container contains water (Container of water). I need it desperately. You would say, Container-3 has water.

So in this example, that guy knows that one container contains water, but doesn’t know which container it is. He was using the member name (water) to know the container name or container address. So this is the use of Container_of macro in the Linux kernel.

I know the address of one member in the structure. But I don’t know the address of that structure. That structure may have many members. So we can find the address of the structure using this container_of macro in Linux Kernel.

Hope you understood this concept. If you don’t, you can understand that by using examples.

container_of()

container_of() is a macro defined in <linux/kernel.h>

The syntax of the Macro is given below.

container_of(ptr, type, member)

Where,

ptr – the pointer to the member.

type – the type of container struct this is embedded in.

member – the name of the member within the struct.

It returns the address of the container structure of the member.

We will see the example. This Example is not the Linux kernel example. This is just a C example. I’ll show you the Linux kernel example later in this post.

Container_of Macro – Example

Container_of macro will not be there in the normal C program library. This is the Linux Kernel library. So manually, I’ve added the macro to my program. Container_of macro is used to find the container structure address of the given member.

In this example, I’ve declared one structure that contains two members (mem1, mem2). I’m going to get the structure address by using the member mem2.

#include  <stdio.h>

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

#define container_of(ptr, type, member) ({         \
    const typeof( ((type *)0)->member ) *__mptr = (ptr); \
    (type *)( (char *)__mptr - offsetof(type,member) );})

int main(void)
{
    struct sample {
        int mem1;
        char mem2;
    };
    
    struct sample sample1;
    
    printf("Address of Structure sample1 (Normal Method) = %p\n", &sample1);
    
    printf("Address of Structure sample1 (container_of Method) = %p\n", 
                            container_of(&sample1.mem2, struct sample, mem2));
    
    return 0;
}

Since we know the address of the structure sample1 already. But, for your understanding of the container_of I made a very easy program. Okay, What will be the output? Both addresses should be the same or different?  Let’s see the output.

Output

Address of Structure sample1 (Normal Method) = 0x7ffd0d058780
Address of Structure sample1 (container_of Method) = 0x7ffd0d058780

Yessss!!!!!. Both addresses should be the same. I hope you understood the concept of container_of macro. Do you know how that container of macro works? Start to read more.

Working of the Container_of Macro

The full expansion of this macro is shown below.

/**
 * container_of - cast a member of a structure out to the containing structure
 * @ptr:    the pointer to the member.
 * @type:   the type of the container struct this is embedded in.
 * @member: the name of the member within the struct.
 *
 */
#define container_of(ptr, type, member) ({         \
    const typeof( ((type *)0)->member ) *__mptr = (ptr); \
    (type *)( (char *)__mptr - offsetof(type,member) );})

Did you understand anything by seeing this macro? Okay, we will go step by step. Now we will take the first line.

const typeof( ((type *)0)->member ) *__mptr = (ptr);

Here, everyone knows the use of const. So I’ll explain typeof in this line.

typeof()

Since typeof is a compiler extension, there is not really a definition for it, but in the tradition of C it would be an operator like sizeof. It takes one argument and returns the static type of the operand. Just see the below example.

Example:

#include  <stdio.h>

int main(void)
{
    char name[20] = "EmbeTronicX";
    typeof(name) role = "Tutorial";
    printf("%s %s\n", name, role);
    printf("Size of name = %d, Size of role = %d\n",sizeof(name),sizeof(role));
    return 0;
}

Output:

Here is the output.

EmbeTronicX Tutorial
Size of name = 20, Size of role = 20

If you see the output carefully, the type of the name is assigned to the role.

Zero Point Reference

But what about the zero-pointer dereference? Well, it’s a little pointer magic to get the type of member. It won’t crash, because the expression itself will never be evaluated. All the compiler cares for is its type. The same situation occurs in case we ask back for the address. The compiler again doesn’t care for the value, it will simply add the offset of the member to the address of the structure, in this particular case 0, and return the new address.

Example:

#include  <stdio.h>

int main(void)
{
    struct sample {
        int mem1;
        char mem2;
    };

    printf("Offset of the member = %d\n", &((struct s*)0)->mem2);
    return 0;
}

Output:

Offset of the member = 4

In this example, it returns the offset of the mem2. mem1 will occupy offset 0 to 3. So mem2 will take 4.

So, Also note that the following two definitions are equivalent:

typeof(((struct sample *)0)->mem2) test_var;

char test_var;

Now let’s take the original example that we saw earlier in this post.

const typeof( ((type *)0)->member ) *__mptr = (ptr) this line creates the local constant pointer variable.

const typeof( ((type *)0)->member ) *__mptr = (ptr)—–> const char * __mptr = &sample1.mem2

Now we can take the second line which is (type *)( (char *)__mptr - offsetof(type,member) ).

offsetof()

offsetof is a macro that will return a byte offset of a member to the beginning of the structure. The macro is shown below.

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

It is even part of the standard library (available in stddef.h ).

We have already seen an example of this &((TYPE *)0)->MEMBER).

It returns an address of a member called MEMBER of a structure of type TYPE that is stored in memory from address 0 (which happens to be the offset we’re looking for).

Now we need to convert this both macro (offsetof(), container_of()) based on our original example. These macros will replace our example, like below.

container_of(&sample1.mem2, struct sample, mem2)
            ||
            ||
            \/
const char * __mptr = &sample1.mem2;
struct sample * ((char*)  __mptr - &((struct sample*)0)->mem2)
            ||
            ||
            \/
const char* __mptr = 0x7FFD0D058784;   //(Address of mem2 is 0x7FFD0D058784)
struct sample * (0x7FFD0D058784 - 4)
            ||
            ||
            \/
struct sample* (0x7FFD0D058780)        //This is the address of the container structure

Linux Kernel Driver Example Scenario

This is the macro of the Linux Kernel. The below snippet is an example scenario of container_of used in Linux Kernel driver.

struct foo {
    spinlock_t lock;
    struct workqueue_struct *wq;
    struct work_struct offload;
    (...)
};

static void foo_work(struct work_struct *work)
{
    struct foo *foo = container_of(work, struct foo, offload);

    (...)
}

static irqreturn_t foo_handler(int irq, void *arg)
{
    struct foo *foo = arg;

    queue_work(foo->wq, &foo->offload);
    (...)
}

static int foo_probe(...)
{
    struct foo *foo;

    foo->wq = create_singlethread_workqueue("foo-wq");
    INIT_WORK(&foo->offload, foo_work);
    (...)
}

In our next article, we will see Flexible Array Member in C programming.

You can also read the below tutorials.

Linux Device Driver TutorialsC Programming Tutorials
FreeRTOS TutorialsNuttX RTOS Tutorials
RTX RTOS TutorialsInterrupts Basics
I2C Protocol – Part 1 (Basics)I2C Protocol – Part 2 (Advanced Topics)
STM32 TutorialsLPC2148 (ARM7) Tutorials
PIC16F877A Tutorials8051 Tutorials
Unit Testing in C TutorialsESP32-IDF Tutorials
Raspberry Pi TutorialsEmbedded Interview Topics
Reset Sequence in ARM Cortex-M4BLE Basics
VIC and NVIC in ARMSPI – Serial Peripheral Interface Protocol
STM32F7 Bootloader TutorialsRaspberry PI Pico Tutorials
STM32F103 Bootloader TutorialsRT-Thread RTOS Tutorials
Zephyr RTOS Tutorials - STM32Zephyr RTOS Tutorials - ESP32
AUTOSAR TutorialsUDS Protocol Tutorials
Product ReviewsSTM32 MikroC Bootloader Tutorial
VHDL Tutorials
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

5 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Table of Contents