-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[S390] Add dynamic size check for usercopy functions.
Use a wrapper for copy_to/from_user to chose the best usercopy method. The mvcos instruction is better for sizes greater than 256 bytes, if mvcos is not available a page table walk is better for sizes greater than 1024 bytes. Also removed the redundant copy_to/from_user_std_small functions. Signed-off-by: Gerald Schaefer <geraldsc@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
- Loading branch information
Gerald Schaefer
authored and
Martin Schwidefsky
committed
Dec 4, 2006
1 parent
d57de5a
commit 59f35d5
Showing
4 changed files
with
190 additions
and
59 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
/* | ||
* arch/s390/lib/uaccess_pt.c | ||
* | ||
* User access functions based on page table walks. | ||
* | ||
* Copyright IBM Corp. 2006 | ||
* Author(s): Gerald Schaefer (gerald.schaefer@de.ibm.com) | ||
*/ | ||
|
||
#include <linux/errno.h> | ||
#include <asm/uaccess.h> | ||
#include <linux/mm.h> | ||
#include <asm/futex.h> | ||
|
||
static inline int __handle_fault(struct mm_struct *mm, unsigned long address, | ||
int write_access) | ||
{ | ||
struct vm_area_struct *vma; | ||
int ret = -EFAULT; | ||
|
||
down_read(&mm->mmap_sem); | ||
vma = find_vma(mm, address); | ||
if (unlikely(!vma)) | ||
goto out; | ||
if (unlikely(vma->vm_start > address)) { | ||
if (!(vma->vm_flags & VM_GROWSDOWN)) | ||
goto out; | ||
if (expand_stack(vma, address)) | ||
goto out; | ||
} | ||
|
||
if (!write_access) { | ||
/* page not present, check vm flags */ | ||
if (!(vma->vm_flags & (VM_READ | VM_EXEC | VM_WRITE))) | ||
goto out; | ||
} else { | ||
if (!(vma->vm_flags & VM_WRITE)) | ||
goto out; | ||
} | ||
|
||
survive: | ||
switch (handle_mm_fault(mm, vma, address, write_access)) { | ||
case VM_FAULT_MINOR: | ||
current->min_flt++; | ||
break; | ||
case VM_FAULT_MAJOR: | ||
current->maj_flt++; | ||
break; | ||
case VM_FAULT_SIGBUS: | ||
goto out_sigbus; | ||
case VM_FAULT_OOM: | ||
goto out_of_memory; | ||
default: | ||
BUG(); | ||
} | ||
ret = 0; | ||
out: | ||
up_read(&mm->mmap_sem); | ||
return ret; | ||
|
||
out_of_memory: | ||
up_read(&mm->mmap_sem); | ||
if (current->pid == 1) { | ||
yield(); | ||
goto survive; | ||
} | ||
printk("VM: killing process %s\n", current->comm); | ||
return ret; | ||
|
||
out_sigbus: | ||
up_read(&mm->mmap_sem); | ||
current->thread.prot_addr = address; | ||
current->thread.trap_no = 0x11; | ||
force_sig(SIGBUS, current); | ||
return ret; | ||
} | ||
|
||
static inline size_t __user_copy_pt(unsigned long uaddr, void *kptr, | ||
size_t n, int write_user) | ||
{ | ||
struct mm_struct *mm = current->mm; | ||
unsigned long offset, pfn, done, size; | ||
pgd_t *pgd; | ||
pmd_t *pmd; | ||
pte_t *pte; | ||
void *from, *to; | ||
|
||
done = 0; | ||
retry: | ||
spin_lock(&mm->page_table_lock); | ||
do { | ||
pgd = pgd_offset(mm, uaddr); | ||
if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd))) | ||
goto fault; | ||
|
||
pmd = pmd_offset(pgd, uaddr); | ||
if (pmd_none(*pmd) || unlikely(pmd_bad(*pmd))) | ||
goto fault; | ||
|
||
pte = pte_offset_map(pmd, uaddr); | ||
if (!pte || !pte_present(*pte) || | ||
(write_user && !pte_write(*pte))) | ||
goto fault; | ||
|
||
pfn = pte_pfn(*pte); | ||
if (!pfn_valid(pfn)) | ||
goto out; | ||
|
||
offset = uaddr & (PAGE_SIZE - 1); | ||
size = min(n - done, PAGE_SIZE - offset); | ||
if (write_user) { | ||
to = (void *)((pfn << PAGE_SHIFT) + offset); | ||
from = kptr + done; | ||
} else { | ||
from = (void *)((pfn << PAGE_SHIFT) + offset); | ||
to = kptr + done; | ||
} | ||
memcpy(to, from, size); | ||
done += size; | ||
uaddr += size; | ||
} while (done < n); | ||
out: | ||
spin_unlock(&mm->page_table_lock); | ||
return n - done; | ||
fault: | ||
spin_unlock(&mm->page_table_lock); | ||
if (__handle_fault(mm, uaddr, write_user)) | ||
return n - done; | ||
goto retry; | ||
} | ||
|
||
size_t copy_from_user_pt(size_t n, const void __user *from, void *to) | ||
{ | ||
size_t rc; | ||
|
||
if (segment_eq(get_fs(), KERNEL_DS)) { | ||
memcpy(to, (void __kernel __force *) from, n); | ||
return 0; | ||
} | ||
rc = __user_copy_pt((unsigned long) from, to, n, 0); | ||
if (unlikely(rc)) | ||
memset(to + n - rc, 0, rc); | ||
return rc; | ||
} | ||
|
||
size_t copy_to_user_pt(size_t n, void __user *to, const void *from) | ||
{ | ||
if (segment_eq(get_fs(), KERNEL_DS)) { | ||
memcpy((void __kernel __force *) to, from, n); | ||
return 0; | ||
} | ||
return __user_copy_pt((unsigned long) to, (void *) from, n, 1); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters