Allocating memory for array of struct in linux kernel and got strange output od structs ids

Welcome to Programming Tutorial official website. Today - we are going to cover how to solve / find the solution of this error Allocating memory for array of struct in linux kernel and got strange output od structs ids on this date .

I wrote a program in linux kernel, which should allocate memory for 5 simple structures each of with unique id, so i used static int in a constructor and increment it and in the end i just print messages to the buffer. When i saw the buffer i got a strange result because the ids had values like 0, 64, 128, 192, 256 i was surprised because i thought that i will see the values like 0, 1, 2, 3, 4. Why did i get result like this it is something wrong?

Output:

[ 2653.505140] Example struct id: 0
[ 2653.505143] Example string field content: Test
[ 2653.526565] Example struct id: 64
[ 2653.526568] Example string field content: Test
[ 2653.526623] Example struct id: 128
[ 2653.526625] Example string field content: Test
[ 2653.550439] Example struct id: 192
[ 2653.550443] Example string field content: Test
[ 2653.550513] Example struct id: 256
[ 2653.550514] Example string field content: Test

There is my code:

#include<linux/module.h>
#include<linux/slab.h>
#include<linux/string.h>

static struct example_struct {
    unsigned int id;
    char example_string[10];
} *example_struct_pointer[5];

static struct kmem_cache *example_cachep[5];

static void example_constructor(void *argument)
{
    static unsigned int id;
    static char test_string[] = "Test";
    struct example_struct *example = (struct example_struct *)argument;
    example->id = id;
    strcpy(example->example_string,test_string);
    id++;
}

void print_example_struct(struct example_struct *example)
{
    pr_notice("Example struct id: %dn",example->id);
    pr_notice("Example string field content: %sn",example->example_string);
}

static int __init zad1_init(void)
{
    int i;
    for(i = 0; i < 5; i++){
        example_cachep[i] = kmem_cache_create("example cache", sizeof(struct example_struct),0, SLAB_HWCACHE_ALIGN|SLAB_POISON|SLAB_RED_ZONE, example_constructor);
        if(IS_ERR(example_cachep[i])) {
            pr_alert("Error creating cache: %ldn",PTR_ERR(example_cachep[i]));
            return -ENOMEM;
        }
    }

    for(i = 0; i < 5; i++){
        example_struct_pointer[i] = (struct example_struct *) kmem_cache_alloc(example_cachep[i],GFP_KERNEL);
        if(IS_ERR(example_struct_pointer[i])) {
            pr_alert("Error allocating form cache: %ldn", PTR_ERR(example_struct_pointer[i]));
            kmem_cache_destroy(example_cachep[i]);
            return -ENOMEM;
        }
    }
    
    return 0;
}

static void __exit zad1_exit(void)
{
    int i;
    for(i = 0; i < 5; i++){
        if(example_cachep[i]) {
            if(example_struct_pointer[i]) {
                print_example_struct(example_struct_pointer[i]);
                kmem_cache_free(example_cachep[i],example_struct_pointer[i]);
            }
            kmem_cache_destroy(example_cachep[i]);
        }
    }
}

module_init(zad1_init);
module_exit(zad1_exit);

MODULE_LICENSE("GPL");

Answer

ctor parameter for kmem_cache_create function is not a constructor in the common sense.

There is no guarantee that given function will be called only once and only when the object is requested from the cache. The guarantee is actually the opposite (mm/slab_common.c:389):

* The @ctor is run when new pages are allocated by the cache.

So, it could be much more calls to ctor than the number of objects requested from the cache.

If you simply want to allocate and initialize several objects, then just define corresponding constructor:

// Single cache can be used for allocate multiple object.
// No needs to define a cache-per-object.
static struct kmem_cache* example_cachep;

// Constructor for the object of type 'example_struct'.
static struct example_struct* create_example_object(void)
{
    static unsigned int id;
    struct example_struct *example = kmem_cache_alloc(example_cachep, GFP_KERNEL);
    if (example != NULL)
    {
      example->id = id;
      strcpy(example->example_string, "Test");
      id++;
    }

    return example;
}

static int __init zad1_init(void)
{
  // ...
  // Create the cache.
  example_cachep = kmem_cache_create("example cache", sizeof(struct example_struct),0, SLAB_HWCACHE_ALIGN|SLAB_POISON|SLAB_RED_ZONE, example_constructor);

  // create several 'example' objects.
  for(i = 0; i < 5; i++){
    example_struct_pointer[i] = create_example_object();
    if(example_struct_pointer[i] == NULL)
    {
       // ... Somehow process the allocation failure.
    }
  }

  return 0;
}

ctor parameter is useful to initialize only some fields of the object. Such initialization could save a bit time (slightly improve performance), but only when both following conditions applies:

  1. You actively create (allocate + initialize) and destroy (deallocate) objects from the cache.
  2. The fields you are going to initialize have exactly the same values in just initialized objects and in the object which is going to be deallocated.

For example, you could have a usage_count field in your object, and this field is equal to 0 both when an object is just initialized and when an object is going to be deallocated.

The usefulness of ctor for such fields is that ctor may be NOT called for an allocated object which was previously deallocated from the same cache. That is, you could save a time by not initializing a field which already has a desired value.

Because not initializing a single (or several) values could save only a little time, but ctor could be called more times than it is needed, then you should carefully measure whether you actually gain a performance instead of loosing it.