Merge branch 'x86-pat-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip
* 'x86-pat-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/linux-2.6-tip: x86 PAT: remove CPA WARN_ON for zero pte x86 PAT: return compatible mapping to remap_pfn_range callers x86 PAT: change track_pfn_vma_new to take pgprot_t pointer param x86 PAT: consolidate old memtype new memtype check into a function x86 PAT: remove PFNMAP type on track_pfn_vma_new() error
This commit is contained in:
commit
a6525042bf
|
@ -341,6 +341,25 @@ static inline pgprot_t pgprot_modify(pgprot_t oldprot, pgprot_t newprot)
|
||||||
|
|
||||||
#define canon_pgprot(p) __pgprot(pgprot_val(p) & __supported_pte_mask)
|
#define canon_pgprot(p) __pgprot(pgprot_val(p) & __supported_pte_mask)
|
||||||
|
|
||||||
|
static inline int is_new_memtype_allowed(unsigned long flags,
|
||||||
|
unsigned long new_flags)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Certain new memtypes are not allowed with certain
|
||||||
|
* requested memtype:
|
||||||
|
* - request is uncached, return cannot be write-back
|
||||||
|
* - request is write-combine, return cannot be write-back
|
||||||
|
*/
|
||||||
|
if ((flags == _PAGE_CACHE_UC_MINUS &&
|
||||||
|
new_flags == _PAGE_CACHE_WB) ||
|
||||||
|
(flags == _PAGE_CACHE_WC &&
|
||||||
|
new_flags == _PAGE_CACHE_WB)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef __ASSEMBLY__
|
#ifndef __ASSEMBLY__
|
||||||
/* Indicate that x86 has its own track and untrack pfn vma functions */
|
/* Indicate that x86 has its own track and untrack pfn vma functions */
|
||||||
#define __HAVE_PFNMAP_TRACKING
|
#define __HAVE_PFNMAP_TRACKING
|
||||||
|
|
|
@ -555,10 +555,12 @@ repeat:
|
||||||
if (!pte_val(old_pte)) {
|
if (!pte_val(old_pte)) {
|
||||||
if (!primary)
|
if (!primary)
|
||||||
return 0;
|
return 0;
|
||||||
WARN(1, KERN_WARNING "CPA: called for zero pte. "
|
|
||||||
"vaddr = %lx cpa->vaddr = %lx\n", address,
|
/*
|
||||||
*cpa->vaddr);
|
* Special error value returned, indicating that the mapping
|
||||||
return -EINVAL;
|
* did not exist at this address.
|
||||||
|
*/
|
||||||
|
return -EFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (level == PG_LEVEL_4K) {
|
if (level == PG_LEVEL_4K) {
|
||||||
|
|
|
@ -505,6 +505,35 @@ static inline int range_is_allowed(unsigned long pfn, unsigned long size)
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_STRICT_DEVMEM */
|
#endif /* CONFIG_STRICT_DEVMEM */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Change the memory type for the physial address range in kernel identity
|
||||||
|
* mapping space if that range is a part of identity map.
|
||||||
|
*/
|
||||||
|
static int kernel_map_sync_memtype(u64 base, unsigned long size,
|
||||||
|
unsigned long flags)
|
||||||
|
{
|
||||||
|
unsigned long id_sz;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!pat_enabled || base >= __pa(high_memory))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
id_sz = (__pa(high_memory) < base + size) ?
|
||||||
|
__pa(high_memory) - base :
|
||||||
|
size;
|
||||||
|
|
||||||
|
ret = ioremap_change_attr((unsigned long)__va(base), id_sz, flags);
|
||||||
|
/*
|
||||||
|
* -EFAULT return means that the addr was not valid and did not have
|
||||||
|
* any identity mapping. That case is a success for
|
||||||
|
* kernel_map_sync_memtype.
|
||||||
|
*/
|
||||||
|
if (ret == -EFAULT)
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
int phys_mem_access_prot_allowed(struct file *file, unsigned long pfn,
|
int phys_mem_access_prot_allowed(struct file *file, unsigned long pfn,
|
||||||
unsigned long size, pgprot_t *vma_prot)
|
unsigned long size, pgprot_t *vma_prot)
|
||||||
{
|
{
|
||||||
|
@ -555,9 +584,7 @@ int phys_mem_access_prot_allowed(struct file *file, unsigned long pfn,
|
||||||
if (retval < 0)
|
if (retval < 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (((pfn < max_low_pfn_mapped) ||
|
if (kernel_map_sync_memtype(offset, size, flags)) {
|
||||||
(pfn >= (1UL<<(32 - PAGE_SHIFT)) && pfn < max_pfn_mapped)) &&
|
|
||||||
ioremap_change_attr((unsigned long)__va(offset), size, flags) < 0) {
|
|
||||||
free_memtype(offset, offset + size);
|
free_memtype(offset, offset + size);
|
||||||
printk(KERN_INFO
|
printk(KERN_INFO
|
||||||
"%s:%d /dev/mem ioremap_change_attr failed %s for %Lx-%Lx\n",
|
"%s:%d /dev/mem ioremap_change_attr failed %s for %Lx-%Lx\n",
|
||||||
|
@ -601,12 +628,13 @@ void unmap_devmem(unsigned long pfn, unsigned long size, pgprot_t vma_prot)
|
||||||
* Reserved non RAM regions only and after successful reserve_memtype,
|
* Reserved non RAM regions only and after successful reserve_memtype,
|
||||||
* this func also keeps identity mapping (if any) in sync with this new prot.
|
* this func also keeps identity mapping (if any) in sync with this new prot.
|
||||||
*/
|
*/
|
||||||
static int reserve_pfn_range(u64 paddr, unsigned long size, pgprot_t vma_prot)
|
static int reserve_pfn_range(u64 paddr, unsigned long size, pgprot_t *vma_prot,
|
||||||
|
int strict_prot)
|
||||||
{
|
{
|
||||||
int is_ram = 0;
|
int is_ram = 0;
|
||||||
int id_sz, ret;
|
int ret;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
unsigned long want_flags = (pgprot_val(vma_prot) & _PAGE_CACHE_MASK);
|
unsigned long want_flags = (pgprot_val(*vma_prot) & _PAGE_CACHE_MASK);
|
||||||
|
|
||||||
is_ram = pagerange_is_ram(paddr, paddr + size);
|
is_ram = pagerange_is_ram(paddr, paddr + size);
|
||||||
|
|
||||||
|
@ -625,26 +653,27 @@ static int reserve_pfn_range(u64 paddr, unsigned long size, pgprot_t vma_prot)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
if (flags != want_flags) {
|
if (flags != want_flags) {
|
||||||
free_memtype(paddr, paddr + size);
|
if (strict_prot || !is_new_memtype_allowed(want_flags, flags)) {
|
||||||
printk(KERN_ERR
|
free_memtype(paddr, paddr + size);
|
||||||
"%s:%d map pfn expected mapping type %s for %Lx-%Lx, got %s\n",
|
printk(KERN_ERR "%s:%d map pfn expected mapping type %s"
|
||||||
current->comm, current->pid,
|
" for %Lx-%Lx, got %s\n",
|
||||||
cattr_name(want_flags),
|
current->comm, current->pid,
|
||||||
(unsigned long long)paddr,
|
cattr_name(want_flags),
|
||||||
(unsigned long long)(paddr + size),
|
(unsigned long long)paddr,
|
||||||
cattr_name(flags));
|
(unsigned long long)(paddr + size),
|
||||||
return -EINVAL;
|
cattr_name(flags));
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* We allow returning different type than the one requested in
|
||||||
|
* non strict case.
|
||||||
|
*/
|
||||||
|
*vma_prot = __pgprot((pgprot_val(*vma_prot) &
|
||||||
|
(~_PAGE_CACHE_MASK)) |
|
||||||
|
flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Need to keep identity mapping in sync */
|
if (kernel_map_sync_memtype(paddr, size, flags)) {
|
||||||
if (paddr >= __pa(high_memory))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
id_sz = (__pa(high_memory) < paddr + size) ?
|
|
||||||
__pa(high_memory) - paddr :
|
|
||||||
size;
|
|
||||||
|
|
||||||
if (ioremap_change_attr((unsigned long)__va(paddr), id_sz, flags) < 0) {
|
|
||||||
free_memtype(paddr, paddr + size);
|
free_memtype(paddr, paddr + size);
|
||||||
printk(KERN_ERR
|
printk(KERN_ERR
|
||||||
"%s:%d reserve_pfn_range ioremap_change_attr failed %s "
|
"%s:%d reserve_pfn_range ioremap_change_attr failed %s "
|
||||||
|
@ -689,6 +718,7 @@ int track_pfn_vma_copy(struct vm_area_struct *vma)
|
||||||
unsigned long vma_start = vma->vm_start;
|
unsigned long vma_start = vma->vm_start;
|
||||||
unsigned long vma_end = vma->vm_end;
|
unsigned long vma_end = vma->vm_end;
|
||||||
unsigned long vma_size = vma_end - vma_start;
|
unsigned long vma_size = vma_end - vma_start;
|
||||||
|
pgprot_t pgprot;
|
||||||
|
|
||||||
if (!pat_enabled)
|
if (!pat_enabled)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -702,7 +732,8 @@ int track_pfn_vma_copy(struct vm_area_struct *vma)
|
||||||
WARN_ON_ONCE(1);
|
WARN_ON_ONCE(1);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
return reserve_pfn_range(paddr, vma_size, __pgprot(prot));
|
pgprot = __pgprot(prot);
|
||||||
|
return reserve_pfn_range(paddr, vma_size, &pgprot, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* reserve entire vma page by page, using pfn and prot from pte */
|
/* reserve entire vma page by page, using pfn and prot from pte */
|
||||||
|
@ -710,7 +741,8 @@ int track_pfn_vma_copy(struct vm_area_struct *vma)
|
||||||
if (follow_phys(vma, vma_start + i, 0, &prot, &paddr))
|
if (follow_phys(vma, vma_start + i, 0, &prot, &paddr))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
retval = reserve_pfn_range(paddr, PAGE_SIZE, __pgprot(prot));
|
pgprot = __pgprot(prot);
|
||||||
|
retval = reserve_pfn_range(paddr, PAGE_SIZE, &pgprot, 1);
|
||||||
if (retval)
|
if (retval)
|
||||||
goto cleanup_ret;
|
goto cleanup_ret;
|
||||||
}
|
}
|
||||||
|
@ -741,7 +773,7 @@ cleanup_ret:
|
||||||
* Note that this function can be called with caller trying to map only a
|
* Note that this function can be called with caller trying to map only a
|
||||||
* subrange/page inside the vma.
|
* subrange/page inside the vma.
|
||||||
*/
|
*/
|
||||||
int track_pfn_vma_new(struct vm_area_struct *vma, pgprot_t prot,
|
int track_pfn_vma_new(struct vm_area_struct *vma, pgprot_t *prot,
|
||||||
unsigned long pfn, unsigned long size)
|
unsigned long pfn, unsigned long size)
|
||||||
{
|
{
|
||||||
int retval = 0;
|
int retval = 0;
|
||||||
|
@ -758,14 +790,14 @@ int track_pfn_vma_new(struct vm_area_struct *vma, pgprot_t prot,
|
||||||
if (is_linear_pfn_mapping(vma)) {
|
if (is_linear_pfn_mapping(vma)) {
|
||||||
/* reserve the whole chunk starting from vm_pgoff */
|
/* reserve the whole chunk starting from vm_pgoff */
|
||||||
paddr = (resource_size_t)vma->vm_pgoff << PAGE_SHIFT;
|
paddr = (resource_size_t)vma->vm_pgoff << PAGE_SHIFT;
|
||||||
return reserve_pfn_range(paddr, vma_size, prot);
|
return reserve_pfn_range(paddr, vma_size, prot, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* reserve page by page using pfn and size */
|
/* reserve page by page using pfn and size */
|
||||||
base_paddr = (resource_size_t)pfn << PAGE_SHIFT;
|
base_paddr = (resource_size_t)pfn << PAGE_SHIFT;
|
||||||
for (i = 0; i < size; i += PAGE_SIZE) {
|
for (i = 0; i < size; i += PAGE_SIZE) {
|
||||||
paddr = base_paddr + i;
|
paddr = base_paddr + i;
|
||||||
retval = reserve_pfn_range(paddr, PAGE_SIZE, prot);
|
retval = reserve_pfn_range(paddr, PAGE_SIZE, prot, 0);
|
||||||
if (retval)
|
if (retval)
|
||||||
goto cleanup_ret;
|
goto cleanup_ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -314,17 +314,7 @@ int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma,
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
if (flags != new_flags) {
|
if (flags != new_flags) {
|
||||||
/*
|
if (!is_new_memtype_allowed(flags, new_flags)) {
|
||||||
* Do not fallback to certain memory types with certain
|
|
||||||
* requested type:
|
|
||||||
* - request is uncached, return cannot be write-back
|
|
||||||
* - request is uncached, return cannot be write-combine
|
|
||||||
* - request is write-combine, return cannot be write-back
|
|
||||||
*/
|
|
||||||
if ((flags == _PAGE_CACHE_UC_MINUS &&
|
|
||||||
(new_flags == _PAGE_CACHE_WB)) ||
|
|
||||||
(flags == _PAGE_CACHE_WC &&
|
|
||||||
new_flags == _PAGE_CACHE_WB)) {
|
|
||||||
free_memtype(addr, addr+len);
|
free_memtype(addr, addr+len);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -301,7 +301,7 @@ static inline void ptep_modify_prot_commit(struct mm_struct *mm,
|
||||||
* track_pfn_vma_new is called when a _new_ pfn mapping is being established
|
* track_pfn_vma_new is called when a _new_ pfn mapping is being established
|
||||||
* for physical range indicated by pfn and size.
|
* for physical range indicated by pfn and size.
|
||||||
*/
|
*/
|
||||||
static inline int track_pfn_vma_new(struct vm_area_struct *vma, pgprot_t prot,
|
static inline int track_pfn_vma_new(struct vm_area_struct *vma, pgprot_t *prot,
|
||||||
unsigned long pfn, unsigned long size)
|
unsigned long pfn, unsigned long size)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -332,7 +332,7 @@ static inline void untrack_pfn_vma(struct vm_area_struct *vma,
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
extern int track_pfn_vma_new(struct vm_area_struct *vma, pgprot_t prot,
|
extern int track_pfn_vma_new(struct vm_area_struct *vma, pgprot_t *prot,
|
||||||
unsigned long pfn, unsigned long size);
|
unsigned long pfn, unsigned long size);
|
||||||
extern int track_pfn_vma_copy(struct vm_area_struct *vma);
|
extern int track_pfn_vma_copy(struct vm_area_struct *vma);
|
||||||
extern void untrack_pfn_vma(struct vm_area_struct *vma, unsigned long pfn,
|
extern void untrack_pfn_vma(struct vm_area_struct *vma, unsigned long pfn,
|
||||||
|
|
15
mm/memory.c
15
mm/memory.c
|
@ -1511,6 +1511,7 @@ int vm_insert_pfn(struct vm_area_struct *vma, unsigned long addr,
|
||||||
unsigned long pfn)
|
unsigned long pfn)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
pgprot_t pgprot = vma->vm_page_prot;
|
||||||
/*
|
/*
|
||||||
* Technically, architectures with pte_special can avoid all these
|
* Technically, architectures with pte_special can avoid all these
|
||||||
* restrictions (same for remap_pfn_range). However we would like
|
* restrictions (same for remap_pfn_range). However we would like
|
||||||
|
@ -1525,10 +1526,10 @@ int vm_insert_pfn(struct vm_area_struct *vma, unsigned long addr,
|
||||||
|
|
||||||
if (addr < vma->vm_start || addr >= vma->vm_end)
|
if (addr < vma->vm_start || addr >= vma->vm_end)
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
if (track_pfn_vma_new(vma, vma->vm_page_prot, pfn, PAGE_SIZE))
|
if (track_pfn_vma_new(vma, &pgprot, pfn, PAGE_SIZE))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
ret = insert_pfn(vma, addr, pfn, vma->vm_page_prot);
|
ret = insert_pfn(vma, addr, pfn, pgprot);
|
||||||
|
|
||||||
if (ret)
|
if (ret)
|
||||||
untrack_pfn_vma(vma, pfn, PAGE_SIZE);
|
untrack_pfn_vma(vma, pfn, PAGE_SIZE);
|
||||||
|
@ -1671,9 +1672,15 @@ int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr,
|
||||||
|
|
||||||
vma->vm_flags |= VM_IO | VM_RESERVED | VM_PFNMAP;
|
vma->vm_flags |= VM_IO | VM_RESERVED | VM_PFNMAP;
|
||||||
|
|
||||||
err = track_pfn_vma_new(vma, prot, pfn, PAGE_ALIGN(size));
|
err = track_pfn_vma_new(vma, &prot, pfn, PAGE_ALIGN(size));
|
||||||
if (err)
|
if (err) {
|
||||||
|
/*
|
||||||
|
* To indicate that track_pfn related cleanup is not
|
||||||
|
* needed from higher level routine calling unmap_vmas
|
||||||
|
*/
|
||||||
|
vma->vm_flags &= ~(VM_IO | VM_RESERVED | VM_PFNMAP);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
BUG_ON(addr >= end);
|
BUG_ON(addr >= end);
|
||||||
pfn -= addr >> PAGE_SHIFT;
|
pfn -= addr >> PAGE_SHIFT;
|
||||||
|
|
Loading…
Reference in New Issue