| 176 | } |
| 177 | |
| 178 | void * |
| 179 | pages_map(void *addr, size_t size, size_t alignment, bool *commit) { |
| 180 | assert(alignment >= PAGE); |
| 181 | assert(ALIGNMENT_ADDR2BASE(addr, alignment) == addr); |
| 182 | |
| 183 | #if defined(__FreeBSD__) && defined(MAP_EXCL) |
| 184 | /* |
| 185 | * FreeBSD has mechanisms both to mmap at specific address without |
| 186 | * touching existing mappings, and to mmap with specific alignment. |
| 187 | */ |
| 188 | { |
| 189 | if (os_overcommits) { |
| 190 | *commit = true; |
| 191 | } |
| 192 | |
| 193 | int prot = *commit ? PAGES_PROT_COMMIT : PAGES_PROT_DECOMMIT; |
| 194 | int flags = mmap_flags; |
| 195 | |
| 196 | if (addr != NULL) { |
| 197 | flags |= MAP_FIXED | MAP_EXCL; |
| 198 | } else { |
| 199 | unsigned alignment_bits = ffs_zu(alignment); |
| 200 | assert(alignment_bits > 1); |
| 201 | flags |= MAP_ALIGNED(alignment_bits - 1); |
| 202 | } |
| 203 | |
| 204 | void *ret = mmap(addr, size, prot, flags, -1, 0); |
| 205 | if (ret == MAP_FAILED) { |
| 206 | ret = NULL; |
| 207 | } |
| 208 | |
| 209 | return ret; |
| 210 | } |
| 211 | #endif |
| 212 | /* |
| 213 | * Ideally, there would be a way to specify alignment to mmap() (like |
| 214 | * NetBSD has), but in the absence of such a feature, we have to work |
| 215 | * hard to efficiently create aligned mappings. The reliable, but |
| 216 | * slow method is to create a mapping that is over-sized, then trim the |
| 217 | * excess. However, that always results in one or two calls to |
| 218 | * os_pages_unmap(), and it can leave holes in the process's virtual |
| 219 | * memory map if memory grows downward. |
| 220 | * |
| 221 | * Optimistically try mapping precisely the right amount before falling |
| 222 | * back to the slow method, with the expectation that the optimistic |
| 223 | * approach works most of the time. |
| 224 | */ |
| 225 | |
| 226 | void *ret = os_pages_map(addr, size, os_page, commit); |
| 227 | if (ret == NULL || ret == addr) { |
| 228 | return ret; |
| 229 | } |
| 230 | assert(addr == NULL); |
| 231 | if (ALIGNMENT_ADDR2OFFSET(ret, alignment) != 0) { |
| 232 | os_pages_unmap(ret, size); |
| 233 | return pages_map_slow(size, alignment, commit); |
| 234 | } |
| 235 |
no test coverage detected