int get_user_pages(struct task_struct *tsk, struct mm_struct *mm, unsigned long start, int len, int write, int force, struct page **pages, struct vm_area_struct **vmas) { int i = 0; do { struct vm_area_struct * vma; vma = find_extend_vma(mm, start); if ( !vma || (!force && ((write && (!(vma->vm_flags & VM_WRITE))) || (!write && (!(vma->vm_flags & VM_READ))) ) )) { if (i) return i; return -EFAULT; } spin_lock(&mm->page_table_lock); do { struct page *map; while (!(map = follow_page(mm, start, write))) { spin_unlock(&mm->page_table_lock); switch (handle_mm_fault(mm, vma, start, write)) { case 1: tsk->min_flt++; break; case 2: tsk->maj_flt++; break; case 0: if (i) return i; return -EFAULT; default: if (i) return i; return -ENOMEM; } spin_lock(&mm->page_table_lock); } if (pages) { pages[i] = get_page_map(map); /* FIXME: call the correct function, * depending on the type of the found page */ if (pages[i]) page_cache_get(pages[i]); } if (vmas) vmas[i] = vma; i++; start += PAGE_SIZE; len--; } while(len && start < vma->vm_end); spin_unlock(&mm->page_table_lock); } while(len); return i; }
int map_user_kiobuf(int rw, struct kiobuf *iobuf, unsigned long va, size_t len) { unsigned long ptr, end; int err; struct mm_struct * mm; struct vm_area_struct * vma = 0; struct page * map; int i; int datain = (rw == READ); /* Make sure the iobuf is not already mapped somewhere. */ if (iobuf->nr_pages) return -EINVAL; mm = current->mm; dprintk ("map_user_kiobuf: begin\n"); ptr = va & PAGE_MASK; end = (va + len + PAGE_SIZE - 1) & PAGE_MASK; err = expand_kiobuf(iobuf, (end - ptr) >> PAGE_SHIFT); if (err) return err; down(&mm->mmap_sem); err = -EFAULT; iobuf->locked = 0; iobuf->offset = va & ~PAGE_MASK; iobuf->length = len; i = 0; /* * First of all, try to fault in all of the necessary pages */ while (ptr < end) { if (!vma || ptr >= vma->vm_end) { vma = find_vma(current->mm, ptr); if (!vma) goto out_unlock; if (vma->vm_start > ptr) { if (!(vma->vm_flags & VM_GROWSDOWN)) goto out_unlock; if (expand_stack(vma, ptr)) goto out_unlock; } if (((datain) && (!(vma->vm_flags & VM_WRITE))) || (!(vma->vm_flags & VM_READ))) { err = -EACCES; goto out_unlock; } } if (handle_mm_fault(current->mm, vma, ptr, datain) <= 0) goto out_unlock; spin_lock(&mm->page_table_lock); map = follow_page(ptr); if (!map) { spin_unlock(&mm->page_table_lock); dprintk (KERN_ERR "Missing page in map_user_kiobuf\n"); goto out_unlock; } map = get_page_map(map); if (map) atomic_inc(&map->count); else printk (KERN_INFO "Mapped page missing [%d]\n", i); spin_unlock(&mm->page_table_lock); iobuf->maplist[i] = map; iobuf->nr_pages = ++i; ptr += PAGE_SIZE; } up(&mm->mmap_sem); dprintk ("map_user_kiobuf: end OK\n"); return 0; out_unlock: up(&mm->mmap_sem); unmap_kiobuf(iobuf); dprintk ("map_user_kiobuf: end %d\n", err); return err; }
/* * Please read Documentation/cachetlb.txt before using this function, * accessing foreign memory spaces can cause cache coherency problems. * * Accessing a VM_IO area is even more dangerous, therefore the function * fails if pages is != NULL and a VM_IO area is found. */ int get_user_pages(struct task_struct *tsk, struct mm_struct *mm, unsigned long start, int len, int write, int force, struct page **pages, struct vm_area_struct **vmas) { int i; unsigned int flags; /* * Require read or write permissions. * If 'force' is set, we only require the "MAY" flags. */ flags = write ? (VM_WRITE | VM_MAYWRITE) : (VM_READ | VM_MAYREAD); flags &= force ? (VM_MAYREAD | VM_MAYWRITE) : (VM_READ | VM_WRITE); i = 0; do { struct vm_area_struct * vma; vma = find_extend_vma(mm, start); if ( !vma || (pages && vma->vm_flags & VM_IO) || !(flags & vma->vm_flags) ) return i ? : -EFAULT; spin_lock(&mm->page_table_lock); do { struct page *map; while (!(map = follow_page(mm, start, write))) { spin_unlock(&mm->page_table_lock); switch (handle_mm_fault(mm, vma, start, write)) { case 1: tsk->min_flt++; break; case 2: tsk->maj_flt++; break; case 0: if (i) return i; return -EFAULT; default: if (i) return i; return -ENOMEM; } spin_lock(&mm->page_table_lock); } if (pages) { pages[i] = get_page_map(map); /* FIXME: call the correct function, * depending on the type of the found page */ if (!pages[i]) goto bad_page; page_cache_get(pages[i]); } if (vmas) vmas[i] = vma; i++; start += PAGE_SIZE; len--; } while(len && start < vma->vm_end); spin_unlock(&mm->page_table_lock); } while(len); out: return i; /* * We found an invalid page in the VMA. Release all we have * so far and fail. */ bad_page: spin_unlock(&mm->page_table_lock); while (i--) page_cache_release(pages[i]); i = -EFAULT; goto out; }