diff options
Diffstat (limited to 'arch/sh/mm/mmap.c')
| -rw-r--r-- | arch/sh/mm/mmap.c | 136 |
1 files changed, 132 insertions, 4 deletions
diff --git a/arch/sh/mm/mmap.c b/arch/sh/mm/mmap.c index 931f4d003fa0..1b5fdfb4e0c2 100644 --- a/arch/sh/mm/mmap.c +++ b/arch/sh/mm/mmap.c | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * arch/sh/mm/mmap.c | 2 | * arch/sh/mm/mmap.c |
| 3 | * | 3 | * |
| 4 | * Copyright (C) 2008 Paul Mundt | 4 | * Copyright (C) 2008 - 2009 Paul Mundt |
| 5 | * | 5 | * |
| 6 | * This file is subject to the terms and conditions of the GNU General Public | 6 | * This file is subject to the terms and conditions of the GNU General Public |
| 7 | * License. See the file "COPYING" in the main directory of this archive | 7 | * License. See the file "COPYING" in the main directory of this archive |
| @@ -21,9 +21,26 @@ EXPORT_SYMBOL(shm_align_mask); | |||
| 21 | /* | 21 | /* |
| 22 | * To avoid cache aliases, we map the shared page with same color. | 22 | * To avoid cache aliases, we map the shared page with same color. |
| 23 | */ | 23 | */ |
| 24 | #define COLOUR_ALIGN(addr, pgoff) \ | 24 | static inline unsigned long COLOUR_ALIGN(unsigned long addr, |
| 25 | ((((addr) + shm_align_mask) & ~shm_align_mask) + \ | 25 | unsigned long pgoff) |
| 26 | (((pgoff) << PAGE_SHIFT) & shm_align_mask)) | 26 | { |
| 27 | unsigned long base = (addr + shm_align_mask) & ~shm_align_mask; | ||
| 28 | unsigned long off = (pgoff << PAGE_SHIFT) & shm_align_mask; | ||
| 29 | |||
| 30 | return base + off; | ||
| 31 | } | ||
| 32 | |||
| 33 | static inline unsigned long COLOUR_ALIGN_DOWN(unsigned long addr, | ||
| 34 | unsigned long pgoff) | ||
| 35 | { | ||
| 36 | unsigned long base = addr & ~shm_align_mask; | ||
| 37 | unsigned long off = (pgoff << PAGE_SHIFT) & shm_align_mask; | ||
| 38 | |||
| 39 | if (base + off <= addr) | ||
| 40 | return base + off; | ||
| 41 | |||
| 42 | return base - off; | ||
| 43 | } | ||
| 27 | 44 | ||
| 28 | unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, | 45 | unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr, |
| 29 | unsigned long len, unsigned long pgoff, unsigned long flags) | 46 | unsigned long len, unsigned long pgoff, unsigned long flags) |
| @@ -103,6 +120,117 @@ full_search: | |||
| 103 | addr = COLOUR_ALIGN(addr, pgoff); | 120 | addr = COLOUR_ALIGN(addr, pgoff); |
| 104 | } | 121 | } |
| 105 | } | 122 | } |
| 123 | |||
| 124 | unsigned long | ||
| 125 | arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0, | ||
| 126 | const unsigned long len, const unsigned long pgoff, | ||
| 127 | const unsigned long flags) | ||
| 128 | { | ||
| 129 | struct vm_area_struct *vma; | ||
| 130 | struct mm_struct *mm = current->mm; | ||
| 131 | unsigned long addr = addr0; | ||
| 132 | int do_colour_align; | ||
| 133 | |||
| 134 | if (flags & MAP_FIXED) { | ||
| 135 | /* We do not accept a shared mapping if it would violate | ||
| 136 | * cache aliasing constraints. | ||
| 137 | */ | ||
| 138 | if ((flags & MAP_SHARED) && | ||
| 139 | ((addr - (pgoff << PAGE_SHIFT)) & shm_align_mask)) | ||
| 140 | return -EINVAL; | ||
| 141 | return addr; | ||
| 142 | } | ||
| 143 | |||
| 144 | if (unlikely(len > TASK_SIZE)) | ||
| 145 | return -ENOMEM; | ||
| 146 | |||
| 147 | do_colour_align = 0; | ||
| 148 | if (filp || (flags & MAP_SHARED)) | ||
| 149 | do_colour_align = 1; | ||
| 150 | |||
| 151 | /* requesting a specific address */ | ||
| 152 | if (addr) { | ||
| 153 | if (do_colour_align) | ||
| 154 | addr = COLOUR_ALIGN(addr, pgoff); | ||
| 155 | else | ||
| 156 | addr = PAGE_ALIGN(addr); | ||
| 157 | |||
| 158 | vma = find_vma(mm, addr); | ||
| 159 | if (TASK_SIZE - len >= addr && | ||
| 160 | (!vma || addr + len <= vma->vm_start)) | ||
| 161 | return addr; | ||
| 162 | } | ||
| 163 | |||
| 164 | /* check if free_area_cache is useful for us */ | ||
| 165 | if (len <= mm->cached_hole_size) { | ||
| 166 | mm->cached_hole_size = 0; | ||
| 167 | mm->free_area_cache = mm->mmap_base; | ||
| 168 | } | ||
| 169 | |||
| 170 | /* either no address requested or can't fit in requested address hole */ | ||
| 171 | addr = mm->free_area_cache; | ||
| 172 | if (do_colour_align) { | ||
| 173 | unsigned long base = COLOUR_ALIGN_DOWN(addr-len, pgoff); | ||
| 174 | |||
| 175 | addr = base + len; | ||
| 176 | } | ||
| 177 | |||
| 178 | /* make sure it can fit in the remaining address space */ | ||
| 179 | if (likely(addr > len)) { | ||
| 180 | vma = find_vma(mm, addr-len); | ||
| 181 | if (!vma || addr <= vma->vm_start) { | ||
| 182 | /* remember the address as a hint for next time */ | ||
| 183 | return (mm->free_area_cache = addr-len); | ||
| 184 | } | ||
| 185 | } | ||
| 186 | |||
| 187 | if (unlikely(mm->mmap_base < len)) | ||
| 188 | goto bottomup; | ||
| 189 | |||
| 190 | addr = mm->mmap_base-len; | ||
| 191 | if (do_colour_align) | ||
| 192 | addr = COLOUR_ALIGN_DOWN(addr, pgoff); | ||
| 193 | |||
| 194 | do { | ||
| 195 | /* | ||
| 196 | * Lookup failure means no vma is above this address, | ||
| 197 | * else if new region fits below vma->vm_start, | ||
| 198 | * return with success: | ||
| 199 | */ | ||
| 200 | vma = find_vma(mm, addr); | ||
| 201 | if (likely(!vma || addr+len <= vma->vm_start)) { | ||
| 202 | /* remember the address as a hint for next time */ | ||
| 203 | return (mm->free_area_cache = addr); | ||
| 204 | } | ||
| 205 | |||
| 206 | /* remember the largest hole we saw so far */ | ||
| 207 | if (addr + mm->cached_hole_size < vma->vm_start) | ||
| 208 | mm->cached_hole_size = vma->vm_start - addr; | ||
| 209 | |||
| 210 | /* try just below the current vma->vm_start */ | ||
| 211 | addr = vma->vm_start-len; | ||
| 212 | if (do_colour_align) | ||
| 213 | addr = COLOUR_ALIGN_DOWN(addr, pgoff); | ||
| 214 | } while (likely(len < vma->vm_start)); | ||
| 215 | |||
| 216 | bottomup: | ||
| 217 | /* | ||
| 218 | * A failed mmap() very likely causes application failure, | ||
| 219 | * so fall back to the bottom-up function here. This scenario | ||
| 220 | * can happen with large stack limits and large mmap() | ||
| 221 | * allocations. | ||
| 222 | */ | ||
| 223 | mm->cached_hole_size = ~0UL; | ||
| 224 | mm->free_area_cache = TASK_UNMAPPED_BASE; | ||
| 225 | addr = arch_get_unmapped_area(filp, addr0, len, pgoff, flags); | ||
| 226 | /* | ||
| 227 | * Restore the topdown base: | ||
| 228 | */ | ||
| 229 | mm->free_area_cache = mm->mmap_base; | ||
| 230 | mm->cached_hole_size = ~0UL; | ||
| 231 | |||
| 232 | return addr; | ||
| 233 | } | ||
| 106 | #endif /* CONFIG_MMU */ | 234 | #endif /* CONFIG_MMU */ |
| 107 | 235 | ||
| 108 | /* | 236 | /* |
