SYSTEM CALL

1 系統呼叫

使用者程式要顯示字串訊息,就要呼叫系統函式 printf。
使用者程式要使用設備驅動程式,就要呼叫 open、close 等系統函式。
沒有呼叫任何系統程式的使用者程式,大概也不能做任何事。
系統函式通常是透過系統呼叫,系統呼叫通常是軟體中斷,用於實現使用者模式和核心模式的轉換。
80x86 使用 int 0x80,實現系統呼叫。ARM 是用 SWI 實現系統呼叫。
這裡筆者會說明 SWI 實現系統呼叫的過程。

s3c2440

中斷向量表,當各種中斷發生時,程式計數器會自動跳躍到中斷表內,再跳躍到中斷處理程序開頭,準備執行中斷處理函式。
這裡的重點是軟體中斷向量,即 ldr pc,.LCvswi + stubs_offset。
符號 .LCvswi 的位址上存放軟體中斷處理程序的開頭位址 vector_swi。
vector_swi 放在 entry-common.S,是真正執行軟體中斷的地方。

ARM 中斷向量表。

arch\arm\kernel\entry-armv.S
01 .globl    __vectors_start
01 __vectors_start:
01  ARM(    swi    SYS_ERROR0    )
01     W(b)    vector_und + stubs_offset
01     W(ldr)    pc, .LCvswi + stubs_offset
01     W(b)    vector_pabt + stubs_offset
01     W(b)    vector_dabt + stubs_offset
01     W(b)    vector_addrexcptn + stubs_offset
01     W(b)    vector_irq + stubs_offset
01     W(b)    vector_fiq + stubs_offset

.LCvswi 存放軟體中斷處理程序入口點位址 vector_swi。

arch\arm\kernel\entry-armv.S
01 .LCvswi:
01     .word   vector_swi

軟體中斷處理程序。
1.備份所有暫存器值。
2.取得 SWI 指令值,因為指令內容存有軟體中斷號碼。
3.取得系統呼叫向量表 sys_call_table 位址,其內容就是 calls.S 的展開。
4.設定中斷處理函式的回返位址。當處理函式結束後,回返到 ret_fast_syscall,而不是這裡的下一行(add r1,sp,#S_OFF)。
5.跳躍到中斷處理函式所在位址,執行該系統呼叫。

01 ENTRY(vector_swi)
02     sub    sp, sp, #S_FRAME_SIZE
03     stmia    sp, {r0 - r12}
04     ARM(    add    r8, sp, #S_PC    )
05     ARM(    stmdb    r8, {sp, lr}^  ) 
06     ....    
07     ldr    scno, [lr, #-4]
08     ....    
09     adr    tbl, sys_call_table
10     adr    lr, BSYM(ret_fast_syscall) 
11     ldrcc    pc, [tbl, scno, lsl #2] 
12 
13     add    r1, sp, #S_OFF      
14 2:  mov    why, #0             
15     cmp    scno, #(__ARM_NR_BASE - __NR_SYSCALL_BASE)
16     eor    r0, scno, #__NR_SYSCALL_BASE   
17     bcs    arm_syscall    
18     b    sys_ni_syscall    
19 ENDPROC(vector_swi)

系統呼叫向量表 sys_call_table。
1.定義 OBSOLETE(syscall)。舊系統使用 syscall,新系統使用 sys_ni_syscall。
2.設定向量表的入口點符號名稱 sys_call_table。
3.填入向量表內容,即 calls.S。

01 #define ABI(native, compat) native
01 #ifdef CONFIG_AEABI
01 #define OBSOLETE(syscall) sys_ni_syscall
01 #else
01 #define OBSOLETE(syscall) syscall
01 #endif
01 
01 .type    sys_call_table, #object
01 ENTRY(sys_call_table)
01 #include "calls.S"
01 #undef ABI
01 #undef OBSOLETE

向量表內容 calls.S。
1.第0個系統呼叫是 sys_restart_syscall,重新啟動系統。
2.第1個系統呼叫是 sys_exit,結束行程。
3.第2個系統呼叫是 sys_fork_wrapper,行程建立。
4.第3個系統呼叫是 sys_read,設備讀取。
5.第4個系統呼叫是 sys_write,設備寫入。
6.第4個系統呼叫是 sys_open,設備開啟。
7.第4個系統呼叫是 sys_open,設備關閉。

0 CALL(sys_restart_syscall)
1 CALL(sys_exit)
2 CALL(sys_fork_wrapper)
3 CALL(sys_read)
4 CALL(sys_write)
5 CALL(sys_open)
5 CALL(sys_close)
6~364...

這裡以系統呼叫 sys_open 為例,說明系統呼叫建立過程。
程式檔案 linux/fx/open.c

sys_open 的函式實作。
SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, int, mode) 會轉譯成
asmlinkage long sys_open(const char __user * filename,int flags,int mode)
其中,sys_open 就是 open 系統呼叫的入口點符號。

01 SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, int, mode){
01     long ret;
01 
01     if (force_o_largefile()) flags |= O_LARGEFILE;
01     ret = do_sys_open(AT_FDCWD, filename, flags, mode);
01     asmlinkage_protect(3, ret, filename, flags, mode);
01     return ret;
01 }

當 sys_open 執行完畢,回返時,程式就會回返到先前在 vector_swi 設置的 ret_fast_syscall。 ret_fast_syscall 可以立即回到原本的使用者程序,或者切換到其它程序,端看行程的參數設定值。 就算切換到其它程序,還是會再切換回來,只是需要花一些時間慢了一點。這就是 ARM LINUX 系統呼叫的整個執行過程。

01 ret_fast_syscall:
01  UNWIND(.fnstart    )
01  UNWIND(.cantunwind    )
01     disable_irq
01     ldr    r1, [tsk, #TI_FLAGS]
01     tst    r1, #_TIF_WORK_MASK
01     bne    fast_work_pending
01     arch_ret_to_user r1, lr
01     restore_user_regs fast = 1, offset = S_OFF
01     UNWIND(.fnend    )
01 
01 fast_work_pending:
01     str    r0, [sp, #S_R0+S_OFF]!
01 work_pending:
01     tst    r1, #_TIF_NEED_RESCHED
01     bne    work_resched
01     tst    r1, #_TIF_SIGPENDING|_TIF_NOTIFY_RESUME
01     beq    no_work_pending
01     mov    r0, sp
01     mov    r2, why
01     bl    do_notify_resume
01     b    ret_slow_syscall
01 
01 work_resched:
01     bl    schedule
01     ...


2 sys_open

系統呼叫函式 sys_open 是開啟設備驅動程式的函式。函式位於 fs/open.c。
1.確認是否為 32 位元系統,不是 32 位元系統時要設置 O_LARGEFILE 旗號。
2.呼叫 do_sys_open 開啟裝置檔案。
3.巨集 asmlinkage_protect 是用來預防編譯器對 stack 做出錯誤的動作。這是因為參數存放在堆疊。

01 SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, int, mode){
01     long ret;
01 
01     if (force_o_largefile()) flags |= O_LARGEFILE;
01     ret = do_sys_open(AT_FDCWD, filename, flags, mode);
01     asmlinkage_protect(3, ret, filename, flags, mode);
01     return ret;
01 }

include/linux/fcntl.h
01 #define force_o_largefile() (BITS_PER_LONG != 32)

裝置檔案開啟步驟。 1.呼叫 getname,取得檔案名稱字串,存入一個核心記憶體分頁,例如 "/dev/leds"。
2.呼叫 PTR_ERR,取得指標值,有可能為負,用於回返錯誤。
3.呼叫 IS_ERR,判斷指標是否為合理值。為合理值,進行檔按開啟程序。
4.呼叫 get_unused_fd_flags,取得一個符合 flags 條件的檔案開啟表結構(files_struct)記憶體位址。files_struct 是用於記錄一個行程所使用的所有檔案。
5.第 4 步驟成功後,執行檔案開啟。
6.如果檔案開啟失敗,呼叫 put_unused_fd 釋放檔案開啟表結構記憶體,並將錯誤碼紀錄於 fd。
7.如果檔案開啟成功,呼叫 fsnotify_open 通知一個檔案已開啟。
8.將該檔案記錄到檔案群結構,。
9.呼叫 putname,將之前存放檔案名稱內容的核心記憶體分頁釋放掉。

fs/open.c
01 long do_sys_open(int dfd, const char __user *filename, int flags, int mode){
01     char *tmp = getname(filename);
01     int fd = PTR_ERR(tmp);
01 
01     if (!IS_ERR(tmp)) {
01         fd = get_unused_fd_flags(flags);
01         if (fd >= 0) {
01             struct file *f = do_filp_open(dfd, tmp, flags, mode, 0);
01             if (IS_ERR(f)) {
01                 put_unused_fd(fd);
01                 fd = PTR_ERR(f);
01             } else {
01                 fsnotify_open(f->f_path.dentry);
01                 fd_install(fd, f);
01             }
01         }
01         putname(tmp);
01     }
01     return fd;
01 }

函式 getname 是用於在核心記憶體分頁空間中取得一個用來存放名稱字串的分頁。
1.呼叫 __getname,取得核心記憶體分頁起點,有鎖住核心記憶體??。
2.呼叫 do_getname,將檔案內容寫入核心記憶體。
3.當檔案內容寫入失敗,呼叫 __putname,歸還核心記憶體。
4.將核心記憶體目分頁起點寫入 audit 列表。result 有可能是負值,那就不會做任何事。

01 fs/namei.c
01 char *getname(const char __user * filename)
01 {
01     char *tmp, *result;
01 
01     result = ERR_PTR(-ENOMEM);
01     tmp = __getname();
01     if (tmp)  {
01         int retval = do_getname(filename, tmp);
01         result = tmp;
01         if (retval < 0) {
01             __putname(tmp);
01             result = ERR_PTR(retval);
01         }
01     }
01     audit_getname(result);
01     return result;
01 }

函式 do_getname 是用於將名稱字串複製到核心記憶體分頁。
1.呼叫 get_fs,檢查是否與 kernel 共用 data segment ??。
2.檢查檔案名稱位址是否太大。
3.計算容許的最大檔案名稱長度。
4.複製檔案名稱內容。

01 fs/namei.c
01 static int do_getname(const char __user *filename, char *page)
01 {
01     int retval;
01     unsigned long len = PATH_MAX;
01 
01     if (!segment_eq(get_fs(), KERNEL_DS)) {
01         if ((unsigned long) filename >= TASK_SIZE)
01             return -EFAULT;
01         if (TASK_SIZE - (unsigned long) filename < PATH_MAX)
01             len = TASK_SIZE - (unsigned long) filename;
01     }
01 
01     retval = strncpy_from_user(page, filename, len);
01     if (retval > 0) {
01         if (retval < len)
01             return 0;
01         return -ENAMETOOLONG;
01     } else if (!retval)
01         retval = -ENOENT;
01     return retval;
01 }

01 get_unused_fd_flags
01 do_filp_open
01 fsnotify_open
01 fd_install

關於 get_unused_fd_flags。 get_unused_fd_flags 是函式 alloc_fd 的再定義。
函式 alloc_fd 的實在是在 fs/file.c。

01 include/linux/file.h
01 #define get_unused_fd_flags(flags) alloc_fd(0, (flags))

函式 alloc_fd 的功能是從核心記憶體取的一個檔案開啟表結構(files_struct)的記憶體。
此結構是用來紀錄所有被同一行程使用的檔案鏈結。
1.取得此行程的檔案開啟表結構
2.鎖住檔案開啟表結構。
3.取得檔案開啟表佇列。
4.設定開始點為 0。
5.取得下一個 fd 值。
6.當還小於達檔案開啟表佇列的最大值,尋找空白的 fd 位址值。此時 fd 的性質已經從號碼變成結構位址。
7.展開檔案開啟表,回返值為負表示有錯誤,回返錯誤碼。回返值為正但不為0,繼續尋找合乎條件的檔案開啟表。回返值為 0,表示找到該檔案目錄??
8.呼叫 FD_SET,建立 fd 與 fdt->open_fds 的關聯。
9.因為 falgs 為 0,呼叫 FD_CLR,清除 fd 與 fdt->close_on_exec 的關聯。
10.呼叫 rcu_dereference ?? 11.釋放檔案開啟表結構。 12.回返 fd 的位址值。

01 fs/file.c
01 int alloc_fd(unsigned start, unsigned flags)
01 {
01     struct files_struct *files = current->files;
01     unsigned int fd;
01     int error;
01     struct fdtable *fdt;
01 
01     spin_lock(&files->file_lock);
01 repeat:
01     fdt = files_fdtable(files);
01     fd = start;
01     if (fd < files->next_fd) fd = files->next_fd;
01     if (fd < fdt->max_fds) fd = find_next_zero_bit(fdt->open_fds->fds_bits,fdt->max_fds, fd);
01     error = expand_files(files, fd);
01     if (error < 0) goto out;
01     if (error) goto repeat;
01     if (start <= files->next_fd) files->next_fd = fd + 1;
01     FD_SET(fd, fdt->open_fds);
01     if (flags & O_CLOEXEC) FD_SET(fd, fdt->close_on_exec);
01     else FD_CLR(fd, fdt->close_on_exec);
01     error = fd;
01 #if 1
01     if (rcu_dereference(fdt->fd[fd]) != NULL) {
01         printk(KERN_WARNING "alloc_fd: slot %d not NULL!\n", fd);
01         rcu_assign_pointer(fdt->fd[fd], NULL);
01     }
01 #endif
01 out:
01     spin_unlock(&files->file_lock);
01     return error;
01 }

檔案開啟表結構。 比較重要的是
1.檔案鎖 file_lock 2.檔案指標陣列 fd_array,用於紀錄已開啟的檔案。

01 include/linux/fdtable.h
01 struct files_struct {
01     atomic_t count;
01     struct fdtable *fdt;
01     struct fdtable fdtab;
01     spinlock_t file_lock ____cacheline_aligned_in_smp;
01     int next_fd;
01     struct embedded_fd_set close_on_exec_init;
01     struct embedded_fd_set open_fds_init;
01     struct file * fd_array[NR_OPEN_DEFAULT];
01 };

檔案結構。
比較重要的是
1.檔案操作函式結構 file_operations,紀錄關於此檔案所支援的操作方式,如 open、close、ioctl。

01 include/linux/fs.h
01 struct file {
01     union {
01         struct list_head    fu_list;
01         struct rcu_head     fu_rcuhead;
01     } f_u;
01     struct path        f_path;
01 #define f_dentry    f_path.dentry
01 #define f_vfsmnt    f_path.mnt
01     const struct file_operations    *f_op;
01     spinlock_t        f_lock;  
01     atomic_long_t        f_count;
01     unsigned int         f_flags;
01     fmode_t            f_mode;
01     loff_t            f_pos;
01     struct fown_struct    f_owner;
01     const struct cred    *f_cred;
01     struct file_ra_state    f_ra;
01     u64            f_version;
01 #ifdef CONFIG_SECURITY
01     void            *f_security;
01 #endif
01     void            *private_data;
01 #ifdef CONFIG_EPOLL
01     struct list_head    f_ep_links;
01 #endif
01     struct address_space    *f_mapping;
01 #ifdef CONFIG_DEBUG_WRITECOUNT
01     unsigned long f_mnt_write_state;
01 #endif
01 };


3 sys_close

SYSCALL_DEFINE1(close, unsigned int, fd) 會轉譯成
asmlinkage long sys_close(unsigned int fd)

系統呼叫函式 sys_close 是關閉設備驅動程式的函式。函式的實作位於 fs/open.c。
1.取得檔案開啟表鎖。
2.取得檔案開啟表。
3.如果 fd 值太大,表示錯誤,函式跳至,提早回返。
4.依據 fd 取得存放於檔案開啟表內的檔案指標。
5.檔案指標為空指標,函式回返。
6.設定 rcu 指標。rcu 是 read copy update 的簡寫。
7.清除 檔案結構和fdt 的關聯。
8.將 fd 放回到未使用的 fd 佇列。
9.釋放檔案開啟表鎖。
10.關閉檔案。
11.如果關閉檔案有有錯誤,取得錯誤碼。
12.函式回返。
13.函式提早回返程序,釋放檔案鎖。

01 fs/open.c
01 SYSCALL_DEFINE1(close, unsigned int, fd)
01 {
01     struct file * filp;
01     struct files_struct *files = current->files;
01     struct fdtable *fdt;
01     int retval;
01 
01     spin_lock(&files->file_lock);
01     fdt = files_fdtable(files);
01     if (fd >= fdt->max_fds) goto out_unlock;
01     filp = fdt->fd[fd];
01     if (!filp) goto out_unlock;
01     rcu_assign_pointer(fdt->fd[fd], NULL);
01     FD_CLR(fd, fdt->close_on_exec);
01     __put_unused_fd(files, fd);
01     spin_unlock(&files->file_lock);
01     retval = filp_close(filp, files);
01     if (unlikely(retval == -ERESTARTSYS ||
01              retval == -ERESTARTNOINTR ||
01              retval == -ERESTARTNOHAND ||
01              retval == -ERESTART_RESTARTBLOCK))
01         retval = -EINTR;
01     return retval;
01 out_unlock:
01     spin_unlock(&files->file_lock);
01     return -EBADF;
01 }


3 sys_ioctl

系統呼叫函式 sys_ioctl 是關閉設備驅動程式的函式。函式的實作位於 fs/open.c。
1.呼叫 fget_light,取得檔案結構位址。失敗時,跳至 out,回返錯誤碼
1.呼叫 security_file_ioctl,??。
1.呼叫 do_vfs_ioctl,執行 ioctl 操作函式。
1.。
1.。
1.。
1.。

SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, unsigned long, arg) 會轉譯成
asmlinkage long sys_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg)

01 fs/ioctl.c
01 SYSCALL_DEFINE3(ioctl, unsigned int, fd, unsigned int, cmd, unsigned long, arg)
01 {
01     struct file *filp;
01     int error = -EBADF;
01     int fput_needed;
01 
01     filp = fget_light(fd, &fput_needed);
01     if (!filp) goto out;
01     error = security_file_ioctl(filp, cmd, arg);
01     if (error) goto out_fput;
01     error = do_vfs_ioctl(filp, fd, cmd, arg);
01  out_fput:
01     fput_light(filp, fput_needed);
01  out:
01     return error;
01 }

九個系統定義的常數都是數字很大的常數,包括
FIOCLEX、FIONCLEX、FIONBIO、FIOASYNC、FIOQSIZE、FIFREEZE、FITHAW、FS_IOC_FIEMAP、FIGETBSZ。

使用者可以自行定義幾個 ioctl 命令,如 01.2.3.4...。這些命令會落入到 default,由 file_ioctl 或 vfs_ioctl 執行。
file_ioctl 是當檔案是系統上的一個 inode 時呼叫的函式,反之則呼叫 vfs_ioctl。
呼叫 file_ioctl 最後也可能會呼叫到 vfs_ioctl。

01 fs/ioctl.c
01 int do_vfs_ioctl(struct file *filp, unsigned int fd, unsigned int cmd, unsigned long arg)
01 {
01     int error = 0;
01     int __user *argp = (int __user *)arg;
01 
01     switch (cmd) {
01     case FIOCLEX:
01         set_close_on_exec(fd, 1);
01         break;
01     case FIONCLEX:
01         set_close_on_exec(fd, 0);
01         break;
01     case FIONBIO:
01         error = ioctl_fionbio(filp, argp);
01         break;
01     case FIOASYNC:
01         error = ioctl_fioasync(fd, filp, argp);
01         break;
01     case FIOQSIZE:
01         if (S_ISDIR(filp->f_path.dentry->d_inode->i_mode) ||
01             S_ISREG(filp->f_path.dentry->d_inode->i_mode) ||
01             S_ISLNK(filp->f_path.dentry->d_inode->i_mode)) {
01             loff_t res =
01                 inode_get_bytes(filp->f_path.dentry->d_inode);
01             error = copy_to_user((loff_t __user *)arg, &res,
01                          sizeof(res)) ? -EFAULT : 0;
01         } else
01             error = -ENOTTY;
01         break;
01     case FIFREEZE:
01         error = ioctl_fsfreeze(filp);
01         break;
01     case FITHAW:
01         error = ioctl_fsthaw(filp);
01         break;
01     case FS_IOC_FIEMAP:
01         return ioctl_fiemap(filp, arg);
01     case FIGETBSZ:
01     {
01         struct inode *inode = filp->f_path.dentry->d_inode;
01         int __user *p = (int __user *)arg;
01         return put_user(inode->i_sb->s_blocksize, p);
01     }
01     default:
01         if (S_ISREG(filp->f_path.dentry->d_inode->i_mode))
01             error = file_ioctl(filp, cmd, arg);
01         else
01             error = vfs_ioctl(filp, cmd, arg);
01         break;
01     }
01     return error;
01 }

01 include/asm-generic/ioctls.h
01 #define FIONBIO	    0x5421
01 #define FIONCLEX    0x5450
01 #define FIOCLEX     0x5451
01 #define FIOASYNC    0x5452
01 #define FIOQSIZE    0x5460

01 include/linux/fs.h
01 #define FIGETBSZ   _IO(0x00,2)
01 #define FIFREEZE    _IOWR('X', 119, int)
01 #define FITHAW        _IOWR('X', 120, int)
01 #define FS_IOC_FIEMAP    _IOWR('f', 11, struct fiemap)

01 include/asm-generic/ioctl.h
01 #define _IOC(dir,type,nr,size) \
01     (((dir)  << _IOC_DIRSHIFT) | \
01      ((type) << _IOC_TYPESHIFT) | \
01      ((nr)   << _IOC_NRSHIFT) | \
01      ((size) << _IOC_SIZESHIFT))
01 
01 #define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)
01 #define _IOWR(type,nr,size)    _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))

1.取得 inode,。
2.當 cmd 是 FIBMAP,呼叫 ioctl_fibmap。
2.當 cmd 是 FIONREAD,呼叫 put_user。
2.當 cmd 是 FS_IOC_RESVSP,呼叫 ioctl_preallocate。
2.當 cmd 是 FS_IOC_RESVSP64,呼叫 ioctl_preallocate。
2.當 cmd 是不是以上四個,呼叫 vfs_ioctl。
,。
,。

01 fs/ioctl.c
01 static int file_ioctl(struct file *filp, unsigned int cmd,unsigned long arg)
01 {
01 	struct inode *inode = filp->f_path.dentry->d_inode;
01 	int __user *p = (int __user *)arg;
01 	switch (cmd) {
01 	case FIBMAP:
01 		return ioctl_fibmap(filp, p);
01 	case FIONREAD:
01 		return put_user(i_size_read(inode) - filp->f_pos, p);
01 	case FS_IOC_RESVSP:
01 	case FS_IOC_RESVSP64:
01 		return ioctl_preallocate(filp, p);
01 	}
01 	return vfs_ioctl(filp, cmd, arg);
01 }

vfs_ioctl 是 ioctl 的執行函式,分有鎖核心和沒有鎖核心兩種處理方式。其中,以沒有鎖住核心為優先。 1.當檔案操作函式為空指標,函式回返。
2.當操作函式指標 unlocked_ioctl 不為空指標,呼叫 unlocked_ioctl。有錯誤則回返。
3.當操作函式指標 ioctl 不為空指標,呼叫 lock_kernel 鎖住核心。再呼叫 ioctl。最後釋放核心。

一般驅動程式的 ioctl 都是有鎖核心,這樣做比較安全。

01 fs/ioctl.c
01 static long vfs_ioctl(struct file *filp, unsigned int cmd,unsigned long arg)
01 {
01 	int error = -ENOTTY;
01 	if (!filp->f_op) goto out;
01 
01 	if (filp->f_op->unlocked_ioctl) {
01 		error = filp->f_op->unlocked_ioctl(filp, cmd, arg);
01 		if (error == -ENOIOCTLCMD) error = -EINVAL;
01 		goto out;
01 	} else if (filp->f_op->ioctl) {
01 		lock_kernel();
01 		error = filp->f_op->ioctl(filp->f_path.dentry->d_inode,filp, cmd, arg);
01 		unlock_kernel();
01 	}
01  out:
01 	return error;
01 }

,。

01 include/asm-generic/uaccess.h
01 #define put_user(x, ptr)          \
01 ({                                \
01     might_sleep();                \
01     access_ok(VERIFY_WRITE, ptr, sizeof(*ptr)) ?    \
01         __put_user(x, ptr) :      \
01         -EFAULT;                  \
01 })
01 #define __put_user(x, ptr)        \
01 ({                                \
01     __typeof__(*(ptr)) __x = (x); \
01     int __pu_err = -EFAULT;       \
01         __chk_user_ptr(ptr);      \
01     switch (sizeof (*(ptr))) {    \
01     case 1:                       \
01     case 2:                       \
01     case 4:                       \
01     case 8:                       \
01         __pu_err = __put_user_fn(sizeof (*(ptr)),    \
01                      ptr, &__x);  \
01         break;                    \
01     default:                      \
01         __put_user_bad();         \
01         break;                    \
01      }                            \
01     __pu_err;                     \
01 })
01 static inline int __put_user_fn(size_t size, void __user *ptr, void *x)
01 {
01 	size = __copy_to_user(ptr, x, size);
01 	return size ? -EFAULT : size;
01 }

,。

01 fs/ioctl.c
01 static int ioctl_fibmap(struct file *filp, int __user *p)
01 {
01     struct address_space *mapping = filp->f_mapping;
01     int res, block;
01 
01     if (!mapping->a_ops->bmap) return -EINVAL;
01     if (!capable(CAP_SYS_RAWIO)) return -EPERM;
01     res = get_user(block, p);
01     if (res) return res;
01     res = mapping->a_ops->bmap(mapping, block);
01     return put_user(res, p);
01 }

,。

01 fs/ioctl.c
01 int ioctl_preallocate(struct file *filp, void __user *argp)
01 {
01     struct inode *inode = filp->f_path.dentry->d_inode;
01     struct space_resv sr;
01 
01     if (copy_from_user(&sr, argp, sizeof(sr))) return -EFAULT;
01     switch (sr.l_whence) {
01     case SEEK_SET:
01         break;
01     case SEEK_CUR:
01         sr.l_start += filp->f_pos;
01         break;
01     case SEEK_END:
01         sr.l_start += i_size_read(inode);
01         break;
01     default:
01         return -EINVAL;
01     }
01     return do_fallocate(filp, FALLOC_FL_KEEP_SIZE, sr.l_start, sr.l_len);
01 }