A problem has recently been encountered when opening a file with Filp_open, and when opening a file that does not exist, the Filp_open return value is 0xFFFFFFFE instead of 0 (NULL), because the kernel handles the function that returns the pointer with special handling. Functions in the kernel often return pointers, usually with null null pointers if the call goes wrong, but Linux has a more subtle handle that can be represented by the returned pointer.
There must be three cases for any pointer: one is a valid pointer, one is null, a null pointer, one is an error pointer, or an invalid pointer. The so-called error pointer is that it has reached the last page, for example, 32bit system, the kernel space of the highest address 0xFFFFFFFF, then the last page refers to the 0XFFFFF000~0XFFFFFFFF (4 K size page for example). This address is reserved, if more than this address, it must be wrong.
In the linux/err.h contains the processing of this mechanism, mainly through Is_err, Ptr_err, err_ptr a few macros.
/*/#define max_errno 4095#define is_err_value (x) unlikely ((x) >= ( unsigned long)-max_errno)
----
/*Convert the error number to a pointer, as the error number is between -1000~0, the returned pointer falls on the last page*/StaticInlinevoid*err_ptr (Longerror) { return(void*) error;}/*Convert the pointer to an error number*/StaticInlineLongPtr_err (Const void*ptr) { return(Long) ptr;}/*determines whether the returned pointer is an error message or an actual address, that is, if the pointer falls on the last page*/StaticInlineLongIs_err (Const void*ptr)//☆returnIs_err_value ((unsignedLong) (PTR);}
So for pointers returned in the kernel, the way to check for errors is not if (!retptr), but if (Is_err (retptr) or
If (Is_err_value (retptr)).
The following is my understanding of the Is_err function, is not entirely correct, if the understanding of errors, please tell me.
In the Is_err () function (unsigned long) -1000l actually represents the 0x FFFF F000 (because the negative number in the computer is the complement of the original code plus one), in Linux, the allocation of virtual memory space, 0~3G is given to the user space, and 3g~ 4 G is for the Linux kernel, and 0xfffff000 is in the virtual memory space of the Linux kernel, the size from 0xfffff000 to 4G is only 4KB, which is actually the size of a page_size , At this point, if a pointer is in the area of 4KB, then this pointer is not the first address of a page, because it is not enough to allocate a page.
The top 4KB of this kernel virtual space is generally not used as a space for allocation. (I did not find the exact evidence for this, just based on the analysis that follows that the space is reserved and its address range is used for error judgment).
If the argument passed to the Is_err () function is the first address pointer of a page, then it must be an error pointer .
is_err () can also be used to detect an error code, which is used in conjunction with ERR_PTR (), see the following snippet of code: (Kernel/fs/namespace.c/sys_mount ( ))
long sys_mount (charchar char *longvoid * data) { int retval; .... Char *dir_page; .... = GetName (dir_name); = Ptr_err (dir_page); if (Is_err (dir_page)) Goto out1; ....}
GetName () returns the first address of a page that could be an assignment, or it might return Err_ptr (-ENOMEM) because of insufficient memory, first to return to the first address of the page, and then to convert the address of this pointer type to an integer by Ptr_err (), and then through the Is_ ERR () to determine whether a valid page header address is the same as the previous analysis.
Next, if the error code is returned, the value defined by Enomem in Kernel/include/asm-*/error.h is 12, and err_ptr (-ENOMEM) is returned as a pointer type, pointing to 0xfffffff4, As far as the pointer is concerned, it is the top4kb space that points to the virtual kernel space, and the return is false by Is_err ().
In Linux we see the error code Errcode value from 1~???? is unlikely to be greater than 4KB, so through Err_ptr (-errcode), then mapped to the virtual kernel space top4kb (0xfffff000~4g) go, and then through the Is_err () can detect "is error"!
Above, Is_err () can detect the page header address is valid, you can also detect the error code.
Is_err () has some beauty.
Functions in the kernel often return pointers, and the problem is that if they go wrong, they are also expected to be reflected by the returned pointers.
Fortunately, the pointers returned by the kernel are typically points to the boundary of the page (4K boundaries), i.e.
PTR & 0xfff = = 0
So the value of PTR cannot fall between (0XFFFFF000,0XFFFFFFFF),
The general kernel error code is also a small negative number, between 1000 to 0, into a unsigned long,
It's just between (0XFFFFF000,0XFFFFFFFF). So you can use
(unsigned long) ptr > (unsigned long) -1000l
To determine whether the return value of the kernel function is a valid pointer or an error code.
Any of the pointers involved, there must be three cases, one is a valid pointer, one is NULL, a null pointer, one is a wrong pointer, or an invalid pointer. The so-called error pointer means that it has reached the last page. For example, for 32bit systems, Kernel space is the highest address 0xFFFFFFFF, then the last page refers to the 0XFFFFF000~0XFFFFFFFF (assuming 4k a page). This address is reserved, and if it exceeds this address, it must be wrong.
There are several possible errors in the Linux kernel:
Include/asm-generic/errno-base.h file:
#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/* No child processes */
#define EAGAIN/* Try again */
#define ENOMEM/* Out of memory */
#define EACCES/* Permission denied */
#define EFAULT/* Bad address */
#define ENOTBLK/* Block Device required */
#define EBUSY/* Device or resource busy */
#define EEXIST */* File exists */
#define EXDEV */* cross-device link */
#define ENODEV */No such device */
#define ENOTDIR/* Not a directory */
#define EISDIR/* is a directory */
#define EINVAL */* Invalid argument */
#define ENFILE/* File table overflow */
#define EMFILE/* Too many open files */
#define ENOTTY */Not a typewriter */
#define ETXTBSY/* Text file Busy */
#define EFBIG/* File too large */
#define ENOSPC/* No space left on device */
#define ESPIPE/* Illegal seek * *
#define EROFS/* read-only File System */
#define Emlink */Too Many links * *
#define EPIPE */broken pipe */
#define EDOM/* Math argument out of domain of func * *
#define ERANGE/* Math result not representable */
When an error occurs, the-EBUSY,-EINVAL,-ENODEV,-EPIPE,-EAGAIN,-ENOMEM is often returned, and so on, you can see that this value is actually between -1000~0.
For a function that returns a pointer, we usually return null for failure, but this does not indicate the failure (out of memory?). Hardware error or network unreachable? )
So the return time with Err_ptr (-enome) and so on can be judged, because this pointer is clearly illegal
Reference Include/iinux/err.h
Kernel IS_ERR Macro Parsing