1

I am looking at the <linux/kfifo.h>, specifically the DECLARE_KFIFO macro, and I can't figure out why use an union.

#define DECLARE_KFIFO(name, size) \
union { \
    struct kfifo name; \
    unsigned char name##kfifo_buffer[size + sizeof(struct kfifo)]; \
}

My questions are as follows:

  1. What is the point of using an union over here ? What design goal does that solve ? Is it for performance reasons ?
  2. How would you even access this anonymous union ? For example this does not work:

    #include <stdio.h>
    
    int main()
    {
    union {
        int a;
        float b;
    };
    
    a = 10;
    }
    main.c:18:5: error: ‘a’ undeclared (first use in this function)
    a = 10;
    ^
    

Why declare something that cannot be used ?

  1. How does this work with the INIT_KFIFO macro ? How is able to access the union and what relation does it have with the previous macro ?
  2. If my memory serves me correctly, then in C you can only use one one element in an union. So what is going on here ? It will just use one member from the declared union ?

Note: This code is for the 2.6.33 kernel. I know its old code, but the recent kernel 5.6.12 still uses a union in kfifo, only its a lot more complicated.

ng.newbie
  • 2,807
  • 3
  • 23
  • 57
  • 1
    Note that this implementation of the kfifo API was never actually used in practice. If you take a look at the rest of kernel code, nothing used `DECLARE_KFIFO` before it was changed to the current version that is not using an `union`, but a `struct` (as @Tsyvarev explains below). As for why it was declared as an `union` in the first place, you would have to ask the original author ([Stefani Seibold](https://seibold.net/)) directly. It almost looks more like a mistake rather than a meaningful design choice. – Marco Bonelli May 14 '20 at 00:33

1 Answers1

1

The comments to the macro explain almost everything:

/**
 * DECLARE_KFIFO - macro to declare a kfifo and the associated buffer
 * @name: name of the declared kfifo datatype
 * @size: size of the fifo buffer. Must be a power of two.
 *
 * Note1: the macro can be used inside struct or union declaration
 * Note2: the macro creates two objects:
 *  A kfifo object with the given name and a buffer for the kfifo
 *  object named name##kfifo_buffer
 */

This macro is used only as a (some other) structure's or union's field. With such usage the macro creates(allocates in the structure or in the union):

  • a kfifo object which can be accessed as a field with the name name and
  • a buffer for kfifo.

So, structure declaration like

struct my_struct {
   int a;
   char b;
   DECLARE_KFIFO(my_fifo, 100);
};

gives similar effect as

struct my_struct {
   int a;
   char b;
   // This field may be used for call kfifo functions
   struct kfifo my_fifo;
   // This field is never used directly.
   // Pointer to this buffer is stored in the '.buffer' field of kfifo object.
   unsigned char buffer_internal_to_kfifo_implementation[size];
};

It would be more natural for declaring two objects to use a anonymous structure of two fields instead of union:

#define DECLARE_KFIFO(name, size) \
struct { \
    struct kfifo name; \
    unsigned char name##kfifo_buffer[size]; \
}

It should be exactly anonymous for allow direct access to its name field.

Compared to structure's implementation, actual implementation of DECLARE_KFIFO via anonymous union gives the same "field" name and allocates the same amount of bytes (of number size) for its buffer:

union { \
    struct kfifo name; \
    unsigned char name##kfifo_buffer[size + sizeof(struct kfifo)]; \
}

It is difficult to say why they choose union instead of struct.


the recent kernel 5.6.12 still uses a union in kfifo, only its a lot more complicated.

Not quite true. The newer kernel versions uses union for absolutely different purposes:

#define __STRUCT_KFIFO_COMMON(datatype, recsize, ptrtype) \
    union { \
        struct __kfifo  kfifo; \
        datatype    *type; \
        const datatype  *const_type; \
        char        (*rectype)[recsize]; \
        ptrtype     *ptr; \
        ptrtype const   *ptr_const; \
    }

Here kfifo is the only field which is accessed for write/read bytes of data. All other fields are declared just for extract their types using typeof() operator.

So, __STRUCT_KFIFO_COMMON is just a "clever" struct kfifo declaration which knows types of data it contains.

Definition of DECLARE_KFIFO macro

#define DECLARE_KFIFO(fifo, type, size) STRUCT_KFIFO(type, size) fifo

after expanding intermediate macros gives:

#define DECLARE_KFIFO(fifo, type, size) struct { \
    __STRUCT_KFIFO_COMMON(type, recsize, ptrtype); \
    type        buf[((size < 2) || (size & (size - 1))) ? -1 : size]; \
} fifo

So that time it is a field of the structure type with the given name. Similar to the old definition, this structure contain two fields:

  • struct kfifo object (its "clever" version) and
  • a buffer for kfifo.
Tsyvarev
  • 60,011
  • 17
  • 110
  • 153
  • I don't understand what you mean by `__STRUCT_KFIFO_COMMON` is a "clever" `struct kfifo` declaration. **Am I correct when I say that in a union only 1 member can be accessed at a time ?** If so if I access `struct __kfifo` then where does the datatype information go to ? because it seems it would become inaccessible if I do that. also vice-versa, if I access the datatype the data is lost. – ng.newbie May 17 '20 at 15:26
  • "in a union only 1 member can be accessed at a time ?" - No, C language doesn't have such restriction. See e.g. here: https://stackoverflow.com/questions/11373203/accessing-inactive-union-member-and-undefined-behavior. Note, that in a union members share only **data**. So a **type** of a union's member is never lost. – Tsyvarev May 17 '20 at 15:57