LDD3 11 Notes

Download Report

Transcript LDD3 11 Notes

Data Types in the Kernel
Ted Baker  Andy Wang
CIS 4930 / COP 5641
Kernel Data Types

For portability


Should compile with –Wall –Wstrictprototypes 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);



u64 be64_to_cpu(u64);


cpu = internal CPU representation
le = little endian
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


To test whether a returned pointer is
an error code, use


void *ERR_PTR(long error);
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 contains
struct 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 list
struct todo_struct
struct list_head
int priority; /*
/* ... add other
};
{
list;
driver specific */
driver-specific fields */
Linked Lists
More Fun with Linked Lists
C
2
A
3
list_head sorted_by_char
list_head sorted_by_num
What if a structure owns
its own list?
B
1
Can
allocate list
elements
as an array
Linked Lists


The head of the list is usually a
standalone structure
To declare and initialize a list head, call
struct list_head todo_list;
INIT_LIST_HEAD(&todo_list);

To initialize at compile time, call
LIST_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, use
list_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
Example

struct 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
(type *) ((char *)__mptr – offsetof(type, member)); })
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 macros
void 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
*/
list_for_each_safe(struct list_head
struct list_head
struct list_head
*/
the beginning of the loop
*cursor,
*next,
*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)