星期三, 6月 29, 2011

Linux kernel 的 error number 與相關 macro 筆記 (IS_ERR, PTR_ERR, ERR_PTR)


說明:

* linux kernel 裡系統函式的回傳值大都是指標, 其指標返回值會有兩種類型, 1.有效指標, 2.無效指標

* 使用 IS_ERR 來判斷是否有錯誤(無效指標), 有錯誤發生再用 PTR_ERR 把回傳指標轉為錯誤碼.

* linux kernel 利用指標能定址到的最後 4k 記憶體區塊當錯誤判斷, 因為最小的記憶體分頁為 4k.
所以最後一頁邊界等於 ptr_max_addr &= ~0xfff;

* 錯誤碼最大值將不能大於 4k(0xfff).

知道這些前提後, 看一下 include/linux/err.h 裡的宣告。(kernel version: 2.6.29/Android 2.1)


/*
 * Kernel pointers have redundant information, so we can use a
 * scheme where we can return either an error code or a dentry
 * pointer with the same return value.
 *
 * This should be a per-architecture thing, to allow different
 * error and pointer decisions.
 */
#define MAX_ERRNO 4095

#ifndef __ASSEMBLY__

#define IS_ERR_VALUE(x) unlikely((x) >= (unsigned long)-MAX_ERRNO)

static inline void *ERR_PTR(long error)
{
 return (void *) error;
}

static inline long PTR_ERR(const void *ptr)
{
 return (long) ptr;
}

static inline long IS_ERR(const void *ptr)
{
 return IS_ERR_VALUE((unsigned long)ptr);
}

/**
 * ERR_CAST - Explicitly cast an error-valued pointer to another pointer type
 * @ptr: The pointer to cast.
 *
 * Explicitly cast an error-valued pointer to another pointer type in such a
 * way as to make it clear that's what's going on.
 */
static inline void *ERR_CAST(const void *ptr)
{
 /* cast away the const */
 return (void *) ptr;
}

可以看到 maximum error number = 4095 (== 0x0fff == 4K == size of minimum page block).
IS_ERR 先將指標先轉型為 unsigned long 然後呼叫 IS_ERR_VALUE, 將 IS_ERR_VALUE 內容展開為:

unlikely(
(x) >= -4995
)

因為 -4995 轉換為 32 bit hex 為 0xfffff001, 所以是大於或等於 0xfffff001 即被視為錯誤碼.

ERR_PTR (error to pointer) 與 PTR_ERR (pointer to error) 這兩個都只有做轉型, PTR_ERR 比較常用, 一般用 IS_ERR 確定是錯誤值而不是指標後, 就可以用 PTR_ERR 來取得轉型後的錯誤數值, 範例如下:

dev = MKDEV(MISC_MAJOR, misc->minor);

misc->class = class_device_create(misc_class, NULL, dev, misc->dev, "%s", misc->name);
if (IS_ERR(misc->class)) {
        err = PTR_ERR(misc->class);
        goto out;
}

至於錯誤編號的定義, 可以很容易猜出來在 asm/errno.h 裡面. 這部份雖然是依平台不同而定, 但是目前版本裡 (2.6.29) x86 與 arm 都是引入 include/asm-generic/errno.h. 然後再由這個檔案引入 include/asm-generic/errno-base.h, 其中 errno-base.h 包含了一些最基本的定義, 如常見的 -EIO, -EBUSY 等等共有 34 個如下:

#define EPERM   1 /* Operation not permitted */
#define ENOENT   2 /* No such file or directory */
#define ESRCH   3 /* No such process */
#define EINTR   4 /* Interrupted system call */
#define EIO   5 /* I/O error */
#define ENXIO   6 /* No such device or address */
#define E2BIG   7 /* Argument list too long */
#define ENOEXEC   8 /* Exec format error */
#define EBADF   9 /* Bad file number */
#define ECHILD  10 /* No child processes */
#define EAGAIN  11 /* Try again */
#define ENOMEM  12 /* Out of memory */
#define EACCES  13 /* Permission denied */
#define EFAULT  14 /* Bad address */
#define ENOTBLK  15 /* Block device required */
#define EBUSY  16 /* Device or resource busy */
#define EEXIST  17 /* File exists */
#define EXDEV  18 /* Cross-device link */
#define ENODEV  19 /* No such device */
#define ENOTDIR  20 /* Not a directory */
#define EISDIR  21 /* Is a directory */
#define EINVAL  22 /* Invalid argument */
#define ENFILE  23 /* File table overflow */
#define EMFILE  24 /* Too many open files */
#define ENOTTY  25 /* Not a typewriter */
#define ETXTBSY  26 /* Text file busy */
#define EFBIG  27 /* File too large */
#define ENOSPC  28 /* No space left on device */
#define ESPIPE  29 /* Illegal seek */
#define EROFS  30 /* Read-only file system */
#define EMLINK  31 /* Too many links */
#define EPIPE  32 /* Broken pipe */
#define EDOM  33 /* Math argument out of domain of func */
#define ERANGE  34 /* Math result not representable */

沒有留言: