2009年4月20日 星期一

慣C用法

這頁紀錄著一些 kernel 裡面看到的慣C用法:

1. 這種用法好像 C++ 喔!

static inline pte_t native_make_pte(pteval_t val)
{
      return (pte_t) { .pte = val };
}


p.s: pte_t 的定義要看 Phisical Address Extension (PAE) 有沒有打開,打開的話,就是一個 64 bit 的 structure, 沒有打開的話,就是一個 32 bit 的 structure:

#ifdef CONFIG_X86_PAE
  typedef     u64    pteval_t;
  typedef     union {
          struct {
              unsigned long pte_low, pte_high;
          };
          pteval_t pte;
 } pte_t;
...
#else /* !CONFIG_X86_PAE */
typedef unsigned long    pteval_t;
typedef union {
    pteval_t pte;
    pteval_t pte_low;
} pte_t;
#endif


2.常見的 per_cpu()

#define per_cpu(var, cpu)\
          (*SHIFT_PERCPU_PTR(&per_cpu_var(var), per_cpu_offset(cpu)))
其中
#define SHIFT_PERCPU_PTR(__p, __offset)    RELOC_HIDE((__p), (__offset))

#define RELOC_HIDE(ptr, off)                    \
  ({ unsigned long __ptr;                    \
    __asm__ ("" : "=r"(__ptr) : "0"(ptr));        \
    (typeof(ptr)) (__ptr + (off)); })

the `ptr' constraint "0" means `ptr' will use the same constraints with the 0th variable,
i.e. the `__ptr'. Which has the constraints of  `='(output) and `r'(use register for the variable).
This inline asm means : __ptr = (unsigned long) ptr; and calculate the offset
in unit of `long' instead of `typeof(ptr)', finally return the calculated offset with the type `typeof(ptr)'

now let's see the other 2 defines:
#define per_cpu_var(var) per_cpu__##var

extern unsigned long __per_cpu_offset[NR_CPUS];
#define per_cpu_offset(x) (__per_cpu_offset[x])

所以 per_cpu(var,cpu_id) 展開會得到 per_cpu__##var /*注意:雙底線*/ + __per_cpu_offset[cpu_id]

例如 sys_ioperm() 裡面有這一個 C statement:
     tss = &per_cpu(init_tss, get_cpu());
展開 macro 就會得到
     tss = per_cpu__init_tss + __per_cpu_offset[get_cpu()];

3. 超簡潔的 struct 初始化

processor.h 裡頭有這樣的用法:
#define INIT_TSS  {\
    .x86_tss = {\
        .sp0        = sizeof(init_stack) + (long)&init_stack,\
        .ss0        = __KERNEL_DS,\
        .ss1        = __KERNEL_CS,\
        .io_bitmap_base    = INVALID_IO_BITMAP_OFFSET,\
     },\
    .io_bitmap        = { [0 ... IO_BITMAP_LONGS] = ~0 },\
}

這個 INIT_TSS 將來會 assign 給變數型別是 struct tss_struct ,該型別定義如下:

struct tss_struct {
    /*
     * The hardware state:
     */

    struct x86_hw_tss    x86_tss;

    /*
     * The extra 1 is there because the CPU will access an
     * additional byte beyond the end of the IO permission
     * bitmap. The extra byte must be all 1 bits, and must
     * be within the limit.
     */

    unsigned long        io_bitmap[IO_BITMAP_LONGS + 1];
    /*
     * Cache the current maximum and the last task that used the bitmap:
     */

    unsigned long        io_bitmap_max;
    struct thread_struct    *io_bitmap_owner;

    /*
     * Pad the TSS to be cacheline-aligned (size is 0x100):
     */

    unsigned long        __cacheline_filler[35];
    /*
     * .. and then another 0x100 bytes for the emergency kernel stack:
     */

    unsigned long        stack[64];

} __attribute__((packed));

struct x86_hw_tss {
    unsigned short        back_link, __blh;
    unsigned long        sp0;
    unsigned short        ss0, __ss0h;
    unsigned long        sp1;
 ...省略
} __attribute__((packed));


這樣用法相當簡潔,連同 sub structure 的初始化都可以一口氣完成!