Understanding of container_of macro in Linux kernel

The Linux kernel contains a very useful macro named “container_of”. This article is about Understanding of 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.

Understanding of 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 come and ask you that 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 don’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 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 example.

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 the 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 C example. I’ll show you the Linux kernel example later of this post.

Example

Container_of macro will not be there in 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 given member. In this example, I’ve declared one structure which 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 about the container_of I made very easy program. Okay, What will be the output? Both address should be 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 address should be 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? Ok 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 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 the 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 our original example that we have seen earlier of 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 second line which is (type *)( (char *)__mptr - offsetof(type,member) ).

offsetof()

offsetof is a macro which will return a byte offset of a member to the beginning of the structure. The macro 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 the example for 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 these both macro (offsetof(), container_of()) based on our original example. Theses 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 Linux Kernel. Below snippet is the 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);
    (...)
}

 

5 1 vote
Article Rating
Subscribe
Notify of
guest

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

3 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
3
0
Would love your thoughts, please comment.x
Ads Blocker Image Powered by Code Help Pro
Ads Blocker Detected!!!

We have detected that you are using extensions to block ads. Please support us by disabling these ads blocker.

Refresh