36
Data Types in the Kernel Ted Baker Andy Wang CIS 4930 / COP 5641

Data Types in the Kernel Ted Baker Andy Wang CIS 4930 / COP 5641

Embed Size (px)

Citation preview

Page 1: Data Types in the Kernel Ted Baker  Andy Wang CIS 4930 / COP 5641

Data Types in the Kernel

Ted Baker Andy Wang

CIS 4930 / COP 5641

Page 2: Data Types in the Kernel Ted Baker  Andy Wang CIS 4930 / COP 5641

Kernel Data Types

For portability Should compile with –Wall –Wstrict-

prototypes flags Three main classes

Standard C types (e.g., int) Explicitly sized types (e.g., u32) Types for specific kernel objects (e.g.,

pid_t)

Page 3: Data Types in the Kernel Ted Baker  Andy Wang CIS 4930 / COP 5641

Use of Standard C Types

Normal C types are not the same size on all architectures

Try misc-progs/database% misc-progs/datasize

arch Size: char short int long ptr long-long u8 u16 u32 u64

i686 1 2 4 4 4 8 1 2 4 8

Page 4: Data Types in the Kernel Ted Baker  Andy Wang CIS 4930 / COP 5641

Use of Standard C Types

64-bit platforms have different data type representations

arch Size: char short int long ptr long-long u8 u16 u32 u64

i386 1 2 4 4 4 8 1 2 4 8

alpha 1 2 4 8 8 8 1 2 4 8

armv4l 1 2 4 4 4 8 1 2 4 8

ia64 1 2 4 8 8 8 1 2 4 8

m68k 1 2 4 4 4 8 1 2 4 8

mips 1 2 4 4 4 8 1 2 4 8

ppc 1 2 4 4 4 8 1 2 4 8

sparc 1 2 4 4 4 8 1 2 4 8

sparc64 1 2 4 4 4 8 1 2 4 8

x86_64 1 2 4 8 8 8 1 2 4 8

Page 5: Data Types in the Kernel Ted Baker  Andy Wang CIS 4930 / COP 5641

Use of Standard C Types

Knowing that pointers and long integers have the same size Using unsigned long for kernel

addresses prevents unintended pointer dereferencing

Page 6: Data Types in the Kernel Ted Baker  Andy Wang CIS 4930 / COP 5641

Assigning an Explicit Size to Data Items

See <asm/types.h> u8; /* unsigned byte (8-bits) */ u16; /* unsigned word (16-bits) */ u32; /* unsigned 32-bit value */ u64; /* unsigned 64-bit value */

If a user-space program needs to use these types, use __ prefix (e.g., __u8)

Page 7: Data Types in the Kernel Ted Baker  Andy Wang CIS 4930 / COP 5641

Assigning an Explicit Size to Data Items

Kernel also uses conventional types, such as unsigned int Usually done for backward compatibility

Page 8: Data Types in the Kernel Ted Baker  Andy Wang CIS 4930 / COP 5641

Interface-Specific Types

Interface-specific type: defined by a library to provide an interface to specific data structure (e.g., pid_t)

Page 9: Data Types in the Kernel Ted Baker  Andy Wang CIS 4930 / COP 5641

Interface-Specific Types

Many _t types are defined in <linux/types.h> Problematic in printk statements One solution is to cast the value to the

biggest possible type (e.g., unsigned long) Avoids warning messages Will not lose data bits

Page 10: Data Types in the Kernel Ted Baker  Andy Wang CIS 4930 / COP 5641

Other Portability Issues

Be suspicious of explicit constant values

Most values are parameterized

Page 11: Data Types in the Kernel Ted Baker  Andy Wang CIS 4930 / COP 5641

Timer Intervals

Do not assume 1000 jiffies per second Scale times using HZ (number of

interrupts per second) For example, check against a timeout of half

a second, compare the elapsed time against HZ/2

Number of jiffies corresponding to msec second is always msec*HZ/1000

Page 12: Data Types in the Kernel Ted Baker  Andy Wang CIS 4930 / COP 5641

Page Size

Memory page is PAGE_SIZE bytes, not 4KB Can vary from 4KB to 64KB PAGE_SHIFT contains the number of bits

to shift an address to get its page number See <asm/page.h> User-space program can use

getpagesize library function

Page 13: Data Types in the Kernel Ted Baker  Andy Wang CIS 4930 / COP 5641

Page Size

Example To allocate 16KB

Should not specify an order of 2 to get_free_pages

Use get_order#include <asm/page.h>

int order = get_order(16*1024);

buf = get_free_pages(GFP_KERNEL, order);

Page 14: Data Types in the Kernel Ted Baker  Andy Wang CIS 4930 / COP 5641

Byte Order

PC stores multibyte values low-byte first (little-endian)

Some platforms use big-endian Use predefined macros

<linux/byteorder/big_endian.h> <linux/byteorder/little_endian.h>

Page 15: Data Types in the Kernel Ted Baker  Andy Wang CIS 4930 / COP 5641

Byte Order

Examples u32 cpu_to_le32(u32);

cpu = internal CPU representation le = little endian

u64 be64_to_cpu(u64); be = big endian

U16 cpu_to_le16p(u16); p = pointer

Page 16: Data Types in the Kernel Ted Baker  Andy Wang CIS 4930 / COP 5641

Data Alignment

How to read a 4-byte value stored at an address that is not a multiple of 4 bytes? i386 permits this kind of access Not all architectures permit it

Can raise exceptions

Page 17: Data Types in the Kernel Ted Baker  Andy Wang CIS 4930 / COP 5641

Data Alignment

Use the following typeless macros #include <asm/unaligned.h> get_unaligned(ptr); put_unaligned(val, ptr);

Page 18: Data Types in the Kernel Ted Baker  Andy Wang CIS 4930 / COP 5641

Data Alignment

Another issue is the portability of data structures Compiler rearranges structure fields to be

aligned according to platform-specific conventions

Automatically add padding to make things aligned May no longer match the intended format

Page 19: Data Types in the Kernel Ted Baker  Andy Wang CIS 4930 / COP 5641

Data Alignment

Use natural alignment 8-byte items go in an address multiple of

8 Use filler fields that avoid leaving holes in

the data structure Not all platforms align 64-bit values on 64-bit

boundaries Might waste memory

Page 20: Data Types in the Kernel Ted Baker  Andy Wang CIS 4930 / COP 5641

Data Alignment

Tell the compiler to pack the data structure with no fillers added

Example: <linux/edd.h>struct {

u16 id;

u64 lun;

u16 reserved1;

u32 reserved2;

} __attribute__ ((packed)) scsi;

Without __attribute__ ((packed)), lun would be preceded by 2-6 bytes

of fillers

Page 21: Data Types in the Kernel Ted Baker  Andy Wang CIS 4930 / COP 5641

Data Alignment

No compiler optimizations

Some compiler optimizations

__attribute__ ((packed))

Page 22: Data Types in the Kernel Ted Baker  Andy Wang CIS 4930 / COP 5641

Pointers and Error Values

Functions that return pointers cannot report negative error values Return NULL on failure

Some kernel interfaces encode error code in a pointer value Cannot be compared against NULL To use this feature, include

<linux/err.h>

Page 23: Data Types in the Kernel Ted Baker  Andy Wang CIS 4930 / COP 5641

Pointers and Error Values

To return an error, use void *ERR_PTR(long error);

To test whether a returned pointer is an error code, use long IS_ERR(const void *ptr);

To access the error code, use long PTR_ERR(const void *ptr);

Page 24: Data Types in the Kernel Ted Baker  Andy Wang CIS 4930 / COP 5641

Linked Lists

Linux provides a standard implementation of circular, doubly linked lists List functions perform no locking

To use the list mechanism, include <linux/list.h>, which containsstruct list_head {

struct list_head *next, *prev;

};

Page 25: Data Types in the Kernel Ted Baker  Andy Wang CIS 4930 / COP 5641

Linked Lists

To use the Linux list facility Need to embed a list_head in the

structures that make up the liststruct todo_struct {

struct list_head list;

int priority; /* driver specific */

/* ... add other driver-specific fields */

};

Page 26: Data Types in the Kernel Ted Baker  Andy Wang CIS 4930 / COP 5641

Linked Lists

Page 27: Data Types in the Kernel Ted Baker  Andy Wang CIS 4930 / COP 5641

More Fun with Linked Lists

list_head sorted_by_char

list_head sorted_by_num

A

3

B

1

C

2

Can allocate list elements as

an array

What if a structure owns its own list?

Page 28: Data Types in the Kernel Ted Baker  Andy Wang CIS 4930 / COP 5641

Linked Lists

The head of the list is usually a standalone structure

To declare and initialize a list head, callstruct list_head todo_list;

INIT_LIST_HEAD(&todo_list);

To initialize at compile time, callLIST_HEAD(todo_list);

Page 29: Data Types in the Kernel Ted Baker  Andy Wang CIS 4930 / COP 5641

Linked Lists

See <linux/list.h> for a list of list functions

/* add the new entry after the list head */

/* use it to build stacks */

list_add(struct list_head *new, struct list_head *head);

/* add the new entry before the list head (tail) */

/* use it to build FIFO queues */

list_add_tail(struct list_head *new, struct list_head *head);

Page 30: Data Types in the Kernel Ted Baker  Andy Wang CIS 4930 / COP 5641

Linked Lists

/* the given entry is removed from the list */

/* if the entry might be reinserted into another list, call list_del_init */

list_del(struct list_head *entry);

list_del_init(struct list_head *entry);

/* remove the entry from one list and insert into another list */

list_move(struct list_head *entry, struct list_head *head);

list_move_tail(struct list_head *entry,

struct list_head *head);

/* return a nonzero value if the given list is empty */

list_empty(struct list_head *head);

Page 31: Data Types in the Kernel Ted Baker  Andy Wang CIS 4930 / COP 5641

Linked Lists

/* insert a list immediately after head */list_splice(struct list_head *list, struct list_head *head);

To access the data structure itself, uselist_entry(struct list_head *ptr, type_of_struct, field_name);

Same as container_of() ptr is a pointer to a struct

list_head entry

Page 32: Data Types in the Kernel Ted Baker  Andy Wang CIS 4930 / COP 5641

Linked Lists

type_of_struct is the type of the structure containing the ptr

field_name is the name of the list field within the structure

Examplestruct todo_struct *todo_ptr = list_entry(listptr, struct todo_struct, list);

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

checking

Page 33: Data Types in the Kernel Ted Baker  Andy Wang CIS 4930 / COP 5641

Linked Lists

To traverse the linked list, one can follow the prev and next pointers

void todo_add_entry(struct todo_struct *new) {

struct list_head *ptr;

struct todo_struct *entry;

for (ptr = todo_list.next; ptr != &todo_list;

ptr = ptr->next) {

entry = list_entry(ptr, struct todo_struct, list);

if (entry->priority < new->priority) {

list_add_tail(&new->list, ptr);

return;

}

}

list_add_tail(&new->list, &todo_struct)

}

Page 34: Data Types in the Kernel Ted Baker  Andy Wang CIS 4930 / COP 5641

Linked Lists

One can also use predefined macrosvoid todo_add_entry(struct todo_struct *new) {

struct list_head *ptr;

struct todo_struct *entry;

list_for_each(ptr, &todo_list) {

entry = list_entry(ptr, struct todo_struct, list);

if (entry->priority < new->priority) {

list_add_tail(&new->list, ptr);

return;

}

}

list_add_tail(&new->list, &todo_struct)

}

Page 35: Data Types in the Kernel Ted Baker  Andy Wang CIS 4930 / COP 5641

Linked Lists

Predefined macros avoid simple programming errors See <linux/list.h>

/* creates a loop that executes once with cursor pointing at each successive entry */

/* be careful about changing the list while iterating */

list_for_each(struct list_head *cursor,

struct list_head *list)

/* iterates backward */

list_for_each_prev(struct list_head *cursor,

struct list_head *list)

Page 36: Data Types in the Kernel Ted Baker  Andy Wang CIS 4930 / COP 5641

Linked Lists

/* for deleting entries in the list */

/* stores the next entry in next at the beginning of the loop */

list_for_each_safe(struct list_head *cursor,

struct list_head *next,

struct list_head *list)

/* ease the process of dealing with a list containing a given type */

/* no need to call list_entry inside the loop */

list_for_each_entry(type *cursor, struct list_head *list,

member)

list_for_each_entry_safe(type *cursor, type *next,

struct list_head *list, member)