From 7006dff924324c7006f7990e52d5fce1da71f0df Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Wed, 30 Nov 2005 09:35:19 -0800 Subject: [PATCH] --- yaml --- r: 14828 b: refs/heads/master c: a145dd411eb28c83ee4bb68b66f62c326c0f764e h: refs/heads/master v: v3 --- [refs] | 2 +- trunk/include/linux/mm.h | 1 + trunk/mm/memory.c | 36 ++++++++++++++++++++++++++++++++++-- 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/[refs] b/[refs] index 736a6802ee37..35a206351039 100644 --- a/[refs] +++ b/[refs] @@ -1,2 +1,2 @@ --- -refs/heads/master: f8e988436b9d83b20bc5cc378f1a8034816ae6a6 +refs/heads/master: a145dd411eb28c83ee4bb68b66f62c326c0f764e diff --git a/trunk/include/linux/mm.h b/trunk/include/linux/mm.h index 0e73f1539d08..29f02d8513f6 100644 --- a/trunk/include/linux/mm.h +++ b/trunk/include/linux/mm.h @@ -956,6 +956,7 @@ struct page *vmalloc_to_page(void *addr); unsigned long vmalloc_to_pfn(void *addr); int remap_pfn_range(struct vm_area_struct *, unsigned long addr, unsigned long pfn, unsigned long size, pgprot_t); +int vm_insert_page(struct vm_area_struct *, unsigned long addr, struct page *); struct page *follow_page(struct vm_area_struct *, unsigned long address, unsigned int foll_flags); diff --git a/trunk/mm/memory.c b/trunk/mm/memory.c index 8d10b5540c73..4b4fc3a7ea48 100644 --- a/trunk/mm/memory.c +++ b/trunk/mm/memory.c @@ -1172,7 +1172,7 @@ static int insert_page(struct mm_struct *mm, unsigned long addr, struct page *pa spinlock_t *ptl; retval = -EINVAL; - if (PageAnon(page) || !PageReserved(page)) + if (PageAnon(page)) goto out; retval = -ENOMEM; flush_dcache_page(page); @@ -1196,6 +1196,35 @@ static int insert_page(struct mm_struct *mm, unsigned long addr, struct page *pa return retval; } +/* + * This allows drivers to insert individual pages they've allocated + * into a user vma. + * + * The page has to be a nice clean _individual_ kernel allocation. + * If you allocate a compound page, you need to have marked it as + * such (__GFP_COMP), or manually just split the page up yourself + * (which is mainly an issue of doing "set_page_count(page, 1)" for + * each sub-page, and then freeing them one by one when you free + * them rather than freeing it as a compound page). + * + * NOTE! Traditionally this was done with "remap_pfn_range()" which + * took an arbitrary page protection parameter. This doesn't allow + * that. Your vma protection will have to be set up correctly, which + * means that if you want a shared writable mapping, you'd better + * ask for a shared writable mapping! + * + * The page does not need to be reserved. + */ +int vm_insert_page(struct vm_area_struct *vma, unsigned long addr, struct page *page) +{ + if (addr < vma->vm_start || addr >= vma->vm_end) + return -EFAULT; + if (!page_count(page)) + return -EINVAL; + return insert_page(vma->vm_mm, addr, page, vma->vm_page_prot); +} +EXPORT_SYMBOL_GPL(vm_insert_page); + /* * Somebody does a pfn remapping that doesn't actually work as a vma. * @@ -1225,8 +1254,11 @@ static int incomplete_pfn_remap(struct vm_area_struct *vma, if (!pfn_valid(pfn)) return -EINVAL; - retval = 0; page = pfn_to_page(pfn); + if (!PageReserved(page)) + return -EINVAL; + + retval = 0; while (start < end) { retval = insert_page(vma->vm_mm, start, page, prot); if (retval < 0)