/*
* Get kernel address of the user page and pin it.
*/
-static inline struct page *pin_page(unsigned long addr, int write)
+static inline struct page *pin_page(unsigned long addr, int write,
+ unsigned long *pfn)
{
struct mm_struct *mm = current->mm ? : &init_mm;
struct page *page = NULL;
* Do a quick atomic lookup first - this is the fastpath.
*/
retry:
- page = follow_page(mm, addr, write);
+ page = follow_page_pfn(mm, addr, write, pfn);
if (likely(page != NULL)) {
if (!PageReserved(page))
get_page(page);
return page;
}
-
+ if (*pfn)
+ return NULL;
/*
* No luck - bad address or need to fault in the page:
*/
/* ignore errors, just check how much was sucessfully transfered */
while (len) {
struct page *page = NULL;
+ unsigned long pfn = 0;
int bytes, offset;
void *maddr;
- page = pin_page(addr, write);
- if (!page)
+ page = pin_page(addr, write, &pfn);
+ if (!page && !pfn)
break;
bytes = len;
if (bytes > PAGE_SIZE-offset)
bytes = PAGE_SIZE-offset;
- maddr = kmap_atomic(page, KM_USER_COPY);
+ if (page)
+ maddr = kmap_atomic(page, KM_USER_COPY);
+ else
+ maddr = kmap_atomic_nocache_pfn(pfn, KM_USER_COPY);
#define HANDLE_TYPE(type) \
case sizeof(type): *(type *)(maddr+offset) = *(type *)(buf); break;
#undef HANDLE_TYPE
}
kunmap_atomic(maddr, KM_USER_COPY);
- unpin_page(page);
+ if (page)
+ unpin_page(page);
len -= bytes;
buf += bytes;
addr += bytes;
/* ignore errors, just check how much was sucessfully transfered */
while (len) {
int bytes, offset, left, copied;
+ unsigned long pfn = 0;
char *maddr;
- page = pin_page(addr, copy == 2);
- if (!page) {
+ page = pin_page(addr, copy == 2, &pfn);
+ if (!page && !pfn) {
spin_unlock(&mm->page_table_lock);
return -EFAULT;
}
if (bytes > PAGE_SIZE-offset)
bytes = PAGE_SIZE-offset;
- maddr = kmap_atomic(page, KM_USER_COPY);
+ if (page)
+ maddr = kmap_atomic(page, KM_USER_COPY);
+ else
+ maddr = kmap_atomic_nocache_pfn(pfn, KM_USER_COPY);
if (copy == 2) {
memset(maddr + offset, 0, bytes);
copied = bytes;
}
BUG_ON(bytes < 0 || copied < 0);
kunmap_atomic(maddr, KM_USER_COPY);
- unpin_page(page);
+ if (page)
+ unpin_page(page);
len -= copied;
buf += copied;
addr += copied;