Upload
silvester-holmes
View
222
Download
0
Tags:
Embed Size (px)
Citation preview
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)
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
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
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
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)
Assigning an Explicit Size to Data Items
Kernel also uses conventional types, such as unsigned int Usually done for backward compatibility
Interface-Specific Types
Interface-specific type: defined by a library to provide an interface to specific data structure (e.g., pid_t)
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
Other Portability Issues
Be suspicious of explicit constant values
Most values are parameterized
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 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 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);
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>
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
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
Data Alignment
Use the following typeless macros #include <asm/unaligned.h> get_unaligned(ptr); put_unaligned(val, ptr);
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
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
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
Data Alignment
No compiler optimizations
Some compiler optimizations
__attribute__ ((packed))
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>
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);
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;
};
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 */
};
Linked Lists
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?
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);
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);
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);
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
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
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)
}
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)
}
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)
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)