From mboxrd@z Thu Jan 1 00:00:00 1970 Message-Id: <20080118045755.629040000@suse.de> References: <20080118045649.334391000@suse.de> Date: Fri, 18 Jan 2008 15:56:52 +1100 From: npiggin@suse.de Subject: [patch 3/6] mm: add vm_insert_mixed Content-Disposition: inline; filename=mm-insert_mixed.patch Sender: owner-linux-mm@kvack.org Return-Path: To: Linus Torvalds , Andrew Morton Cc: Jared Hulbert , Carsten Otte , Martin Schwidefsky , Heiko Carstens , linux-mm@kvack.org List-ID: vm_insert_mixed will insert either a raw pfn or a refcounted struct page into the page tables, depending on whether vm_normal_page() will return the page or not. With the introduction of the new pte bit, this is now a too tricky for drivers to be doing themselves. filemap_xip uses this in a subsequent patch. Signed-off-by: Nick Piggin Cc: Jared Hulbert Cc: Carsten Otte Cc: Martin Schwidefsky Cc: Heiko Carstens Cc: linux-mm@kvack.org --- include/linux/mm.h | 2 + mm/memory.c | 79 ++++++++++++++++++++++++++++++++++++----------------- 2 files changed, 57 insertions(+), 24 deletions(-) Index: linux-2.6/include/linux/mm.h =================================================================== --- linux-2.6.orig/include/linux/mm.h +++ linux-2.6/include/linux/mm.h @@ -1097,6 +1097,8 @@ int remap_pfn_range(struct vm_area_struc int vm_insert_page(struct vm_area_struct *, unsigned long addr, struct page *); int vm_insert_pfn(struct vm_area_struct *vma, unsigned long addr, unsigned long pfn); +int vm_insert_mixed(struct vm_area_struct *vma, unsigned long addr, + unsigned long pfn); struct page *follow_page(struct vm_area_struct *, unsigned long address, unsigned int foll_flags); Index: linux-2.6/mm/memory.c =================================================================== --- linux-2.6.orig/mm/memory.c +++ linux-2.6/mm/memory.c @@ -1164,8 +1164,9 @@ pte_t * fastcall get_locked_pte(struct m * old drivers should use this, and they needed to mark their * pages reserved for the old functions anyway. */ -static int insert_page(struct mm_struct *mm, unsigned long addr, struct page *page, pgprot_t prot) +static int insert_page(struct vm_area_struct *vma, unsigned long addr, struct page *page, pgprot_t prot) { + struct mm_struct *mm = vma->vm_mm; int retval; pte_t *pte; spinlock_t *ptl; @@ -1224,10 +1225,37 @@ int vm_insert_page(struct vm_area_struct if (!page_count(page)) return -EINVAL; vma->vm_flags |= VM_INSERTPAGE; - return insert_page(vma->vm_mm, addr, page, vma->vm_page_prot); + return insert_page(vma, addr, page, vma->vm_page_prot); } EXPORT_SYMBOL(vm_insert_page); +static int insert_pfn(struct vm_area_struct *vma, unsigned long addr, unsigned long pfn, pgprot_t prot) +{ + struct mm_struct *mm = vma->vm_mm; + int retval; + pte_t *pte, entry; + spinlock_t *ptl; + + retval = -ENOMEM; + pte = get_locked_pte(mm, addr, &ptl); + if (!pte) + goto out; + retval = -EBUSY; + if (!pte_none(*pte)) + goto out_unlock; + + /* Ok, finally just insert the thing.. */ + entry = pte_mkspecial(pfn_pte(pfn, prot)); + set_pte_at(mm, addr, pte, entry); + update_mmu_cache(vma, addr, entry); /* XXX: why not for insert_page? */ + + retval = 0; +out_unlock: + pte_unmap_unlock(pte, ptl); +out: + return retval; +} + /** * vm_insert_pfn - insert single pfn into user vma * @vma: user vma to map to @@ -1243,11 +1271,6 @@ EXPORT_SYMBOL(vm_insert_page); int vm_insert_pfn(struct vm_area_struct *vma, unsigned long addr, unsigned long pfn) { - struct mm_struct *mm = vma->vm_mm; - int retval; - pte_t *pte, entry; - spinlock_t *ptl; - /* * Technically, architectures with pte_special can avoid all these * restrictions (same for remap_pfn_range). However we would like @@ -1260,27 +1283,35 @@ int vm_insert_pfn(struct vm_area_struct BUG_ON((vma->vm_flags & VM_PFNMAP) && is_cow_mapping(vma->vm_flags)); BUG_ON((vma->vm_flags & VM_MIXEDMAP) && pfn_valid(pfn)); - retval = -ENOMEM; - pte = get_locked_pte(mm, addr, &ptl); - if (!pte) - goto out; - retval = -EBUSY; - if (!pte_none(*pte)) - goto out_unlock; + if (addr < vma->vm_start || addr >= vma->vm_end) + return -EFAULT; + return insert_pfn(vma, addr, pfn, vma->vm_page_prot); +} +EXPORT_SYMBOL(vm_insert_pfn); - /* Ok, finally just insert the thing.. */ - entry = pte_mkspecial(pfn_pte(pfn, vma->vm_page_prot)); - set_pte_at(mm, addr, pte, entry); - update_mmu_cache(vma, addr, entry); +int vm_insert_mixed(struct vm_area_struct *vma, unsigned long addr, + unsigned long pfn) +{ + BUG_ON(!(vma->vm_flags & VM_MIXEDMAP)); - retval = 0; -out_unlock: - pte_unmap_unlock(pte, ptl); + if (addr < vma->vm_start || addr >= vma->vm_end) + return -EFAULT; -out: - return retval; + /* + * If we don't have pte special, then we have to use the pfn_valid() + * based VM_MIXEDMAP scheme (see vm_normal_page), and thus we *must* + * refcount the page if pfn_valid is true (hence insert_page rather + * than insert_pfn). + */ + if (!HAVE_PTE_SPECIAL && pfn_valid(pfn)) { + struct page *page; + + page = pfn_to_page(pfn); + return insert_page(vma, addr, page, vma->vm_page_prot); + } + return insert_pfn(vma, addr, pfn, vma->vm_page_prot); } -EXPORT_SYMBOL(vm_insert_pfn); +EXPORT_SYMBOL(vm_insert_mixed); /* * maps a range of physical memory into the requested pages. the old -- -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to majordomo@kvack.org. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: email@kvack.org