On Mon, Jan 23, 2017 at 9:38 AM, Christophe Milard <christophe.mil...@linaro.org> wrote: > _ishm prereserves address space for _ODP_ISHM_SINGLE_VA allocated memory > by reserving unreachable "memory", using PROT_NONE, hence really allocating > virtual space (as the memory cannot be hit). > The problem came when trying to use some of this preallocated space: > strangely, if a new mapping on the preallocated virtual space failed (e.g. > due to lack of huge pages), linux would returned MAP_FAILED (OK) but > also cancel the previous PROT_NONE mapping, hence making the virtual > memory space available for further mmaps. (code Point A) > Before this patch, the code simply re-reserved (PROT_NONE) the area > (point B): > This was NOT OK: yes, _ishm_reserve calls are mutexed, so no other > odpthread 2 could do a reserve between point A and B of opdthread1, but if > thread2 did its own mmap(), malloc(),..., there was a chance for thread 2 > to get virtual space from the preserved area (which should be blocked). > This patch does not allow any A-B window by first doing an mmap (non fixed) > and then performing a second mapping at the correct address (in the > pre-reserved area), which is guaranteed to succeed, and finally removing > the non-fixed mapping. > The non-fix mapping just acts as a failure guard when the proper maping > is done. > Fixes https://bugs.linaro.org/show_bug.cgi?id=2834 > (but very hard to trigger i.e. to prove) > > Signed-off-by: Christophe Milard <christophe.mil...@linaro.org>
Reviewed-and-tested-by: Bill Fischofer <bill.fischo...@linaro.org> > --- > platform/linux-generic/_ishmphy.c | 42 > +++++++++++++++++++++++++++++---------- > 1 file changed, 32 insertions(+), 10 deletions(-) > > diff --git a/platform/linux-generic/_ishmphy.c > b/platform/linux-generic/_ishmphy.c > index 2b2d100..d519af6 100644 > --- a/platform/linux-generic/_ishmphy.c > +++ b/platform/linux-generic/_ishmphy.c > @@ -94,7 +94,7 @@ int _odp_ishmphy_unbook_va(void) > void *_odp_ishmphy_map(int fd, void *start, uint64_t size, > int flags) > { > - void *mapped_addr; > + void *mapped_addr_tmp, *mapped_addr; > int mmap_flags = 0; > > if (flags & _ODP_ISHM_SINGLE_VA) { > @@ -103,15 +103,37 @@ void *_odp_ishmphy_map(int fd, void *start, uint64_t > size, > return NULL; > } > /* maps over fragment of reserved VA: */ > - mapped_addr = mmap(start, size, PROT_READ | PROT_WRITE, > - MAP_SHARED | MAP_FIXED | mmap_flags, fd, > 0); > - /* if mapping fails, re-block the space we tried to take > - * as it seems a mapping failure still affect what was > there??*/ > - if (mapped_addr == MAP_FAILED) { > - mmap_flags = MAP_SHARED | MAP_FIXED | > - MAP_ANONYMOUS | MAP_NORESERVE; > - mmap(start, size, PROT_NONE, mmap_flags, -1, 0); > - mprotect(start, size, PROT_NONE); > + /* first, try a normal map. If that works, remap it where it > + * should (on the prereverved space), and remove the initial > + * normal mapping: > + * This is because it turned out that if a mapping fails > + * on a the prereserved virtual address space, then > + * the prereserved address space which was tried to be mapped > + * on becomes available to the kernel again! This was not > + * according to expectations: the assumption was that if a > + * mapping fails, the system should remain unchanged, but this > + * is obvioulsy not true (at least for huge pages when > + * exhausted). > + * So the strategy is to first map at a non reserved place > + * (which can then be freed and returned to the kernel on > + * failure) and peform a new map to the prereserved space on > + * success (which is then guaranteed to work). > + * The initial free maping can then be removed. > + */ > + mapped_addr = MAP_FAILED; > + mapped_addr_tmp = mmap(NULL, size, PROT_READ | PROT_WRITE, > + MAP_SHARED | mmap_flags, fd, 0); > + if (mapped_addr_tmp != MAP_FAILED) { > + /* If OK, do new map at right fixed location... */ > + mapped_addr = mmap(start, > + size, PROT_READ | PROT_WRITE, > + MAP_SHARED | MAP_FIXED | > mmap_flags, > + fd, 0); > + if (mapped_addr != start) > + ODP_ERR("new map failed:%s\n", > strerror(errno)); > + /* ... and remove initial mapping: */ > + if (munmap(mapped_addr_tmp, size)) > + ODP_ERR("munmap failed:%s\n", > strerror(errno)); > } > } else { > /* just do a new mapping in the VA space: */ > -- > 2.7.4 >