From owner-freebsd-hackers@FreeBSD.ORG Sun Apr 5 18:42:59 2009 Return-Path: Delivered-To: freebsd-hackers@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id E9D881065686 for ; Sun, 5 Apr 2009 18:42:59 +0000 (UTC) (envelope-from fbsdhackers@beasties.demon.nl) Received: from smtp-vbr3.xs4all.nl (smtp-vbr3.xs4all.nl [194.109.24.23]) by mx1.freebsd.org (Postfix) with ESMTP id 6DC2A8FC21 for ; Sun, 5 Apr 2009 18:42:59 +0000 (UTC) (envelope-from fbsdhackers@beasties.demon.nl) Received: from merom.hotsoft.nl (beasties.demon.nl [82.161.3.114]) by smtp-vbr3.xs4all.nl (8.13.8/8.13.8) with ESMTP id n35IQl5M068218; Sun, 5 Apr 2009 20:26:47 +0200 (CEST) (envelope-from fbsdhackers@beasties.demon.nl) Message-ID: <49D8F7E6.8010105@beasties.demon.nl> Date: Sun, 05 Apr 2009 20:26:46 +0200 From: Hans Ottevanger User-Agent: Thunderbird 2.0.0.21 (X11/20090322) MIME-Version: 1.0 To: Kostik Belousov References: <49D89B50.3000304@iae.nl> <20090405155918.GO31897@deviant.kiev.zoral.com.ua> In-Reply-To: <20090405155918.GO31897@deviant.kiev.zoral.com.ua> Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit X-Virus-Scanned: by XS4ALL Virus Scanner Cc: freebsd-hackers@freebsd.org Subject: Re: mlockall() failure and direction for possible solution X-BeenThere: freebsd-hackers@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Technical Discussions relating to FreeBSD List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 05 Apr 2009 18:43:01 -0000 Kostik Belousov wrote: > On Sun, Apr 05, 2009 at 01:51:44PM +0200, Hans Ottevanger wrote: >> Hi folks, >> >> As has been noted before, there is an issue with the mlockall() system >> call always failing on (at least) the amd64 architecture. This is quite >> evident by the automounter (as configured out-of-the-box) printing error >> messages on startup like: >> >> Couldn't lock process pages in memory using mlockall() >> >> I have verified the occurrence of this issue on the amd64 platform on >> 7.1-STABLE and 8.0-CURRENT. On the i386 platform this problem does not >> occur. >> >> To investigate this issue a bit further I ran the following trivial program: >> >> #include >> #include >> #include >> #include >> >> int main(int argc, char *argv[]) >> { >> if (mlockall(MCL_CURRENT|MCL_FUTURE) == -1) >> perror(argv[0]); >> >> char command[80]; >> snprintf(command, 80, "procstat -v %d", getpid()); >> system(command); >> >> exit(0); >> } >> >> which yields (using CURRENT-8.0 as of today, on an Intel DP965LT board >> with a Q6600 and 8 Gbyte RAM, GENERIC kernel stripped of unused devices, >> output folded to 72 characters per line): >> >> /mltest: Resource temporarily unavailable >> PID START END PRT RES PRES REF SHD FL TP >> PATH >> 1064 0x400000 0x401000 r-x 1 0 1 0 CN vn >> /root/mlockall/mltest >> 1064 0x500000 0x501000 rw- 1 0 1 0 CN df >> 1064 0x501000 0x600000 rwx 255 0 1 0 -- df >> 1064 0x800500000 0x80052c000 r-x 44 0 64 31 CN vn >> /libexec/ld-elf.so.1 >> 1064 0x80052c000 0x800534000 rw- 8 0 1 0 C- df >> 1064 0x80062b000 0x800633000 rw- 8 0 1 0 CN vn >> /libexec/ld-elf.so.1 >> 1064 0x800633000 0x80063f000 rw- 12 0 1 0 C- df >> 1064 0x80063f000 0x80072e000 r-x 239 0 128 62 CN vn >> /lib/libc.so.7 >> 1064 0x80072e000 0x80072f000 r-x 1 0 1 0 CN vn >> /lib/libc.so.7 >> 1064 0x80072f000 0x80082f000 r-x 51 0 128 62 CN vn >> /lib/libc.so.7 >> 1064 0x80082f000 0x80084f000 rw- 32 0 1 0 C- vn >> /lib/libc.so.7 >> 1064 0x80084f000 0x800865000 rw- 6 0 1 0 CN df >> 1064 0x800900000 0x800965000 rw- 101 0 1 0 -- df >> 1064 0x800965000 0x800a00000 rw- 155 0 1 0 -- df >> 1064 0x7ffffffe0000 0x800000000000 rwx 3 0 1 0 C- df >> >> I have hunted down the exact location in the kernel where the call to >> mlockall() returns an error (just using printf's, debugging using >> Firewire proved not to be as trivial to set up as it was just a few >> years ago). It appears that while wiring the memory, finally vm_fault() >> is called and it bails out at line 412 of vm_fault.c. The virtual >> address of the page that the system is attempting to wire (argument >> vaddr of vm_fault()) is 0x800762000. From the procstat output above it >> appears that this in the third region backed by /lib/libc.so.7. >> >> This made me think that the issue might be somehow related to the way in >> which dynamic libraries are linked on runtime. Indeed, if above program >> is linked -statically- it does not fail. Also if the program in compiled >> and linked -dynamically- on a i386 platform and run on an amd64, it runs >> successfully. >> >> To make a long story at least a bit shorter, I found that the problem is >> in /usr/src/libexec/rtld_elf/map_object.c at line 156. Here a contiguous >> region is staked out for the code and data. For the amd64, where the >> required alignment of the segments is 1 Mbytes, this causes a region to >> be mapped that is far larger than the library file by which it is >> backed. Addresses that are not backed by the file cannot be resident and >> hence the region cannot be locked into memory. On the i386 architecture >> this problem does not occur since the alignment of the segments is just >> 4 Kbytes. I suspect that the problem also occurs at least on the sparc64 >> architecture. >> >> As a first step to a possible solution you can apply the attached >> (provisional) patch, that uses an anonymous, read-only mapping to create >> the required region. >> >> The output of the above program then becomes: >> >> PID START END PRT RES PRES REF SHD FL TP >> PATH >> 1302 0x400000 0x401000 r-x 1 0 1 0 CN vn >> /root/mlockall/mltest >> 1302 0x500000 0x501000 rw- 1 0 1 0 -- df >> 1302 0x800500000 0x80052c000 r-x 44 0 8 4 CN vn >> /libexec/ld-elf.so.1 >> 1302 0x80052c000 0x800534000 rw- 8 0 1 0 -- df >> 1302 0x80062b000 0x800633000 rw- 8 0 1 0 C- vn >> /libexec/ld-elf.so.1 >> 1302 0x800633000 0x80063f000 rw- 12 0 1 0 -- df >> 1302 0x80063f000 0x80072e000 r-x 239 0 124 62 CN vn >> /lib/libc.so.7 >> 1302 0x80072e000 0x80072f000 r-x 1 0 1 0 C- vn >> /lib/libc.so.7 >> 1302 0x80072f000 0x80082f000 r-- 256 0 1 0 -- df >> 1302 0x80082f000 0x80084f000 rw- 32 0 1 0 C- vn >> /lib/libc.so.7 >> 1302 0x80084f000 0x800865000 rw- 22 0 1 0 -- df >> 1302 0x7ffffffe0000 0x800000000000 rwx 32 0 1 0 -- df >> >> i.e. mlockall() does not return an error anymore. >> >> I still have the following questions: >> >> 1. Is worth the trouble to solve the mlockall() problem at all ? Should >> I file a PR ? > Yes. Do as you want, but I see no reason. > > Your analisys looks correct and useful. > >> 2. Can someone confirm that it also occurs on the other 64 bit >> architectures ? >> >> 3. It might be more elegant to use PROT_NONE instead of PROT_READ when >> just staking out the address space. Currently mlockall() returns an >> error when attempting that, so most likely mlockall() would need to be >> changed to ignore regions mapped with PROT_NONE. On the other hand, the >> pthread implementation uses PROT_NONE to create red zones on the stack >> and mlockall() apparently succeeds with threaded applications (using the >> provided patch). Any opinions/ideas/hints ? > I think that it is better to unmap the holes, instead of making some > mapping. > In that way you free up virtual address space and make it available to the next call to mmap() with the first argument set to zero (i.e. where the caller does not care about the exact location), if the requested space fits in the hole you left. In this way unrelated mappings could end up between the regions of you dynamic libraries. I don't think that would be desirable. Using PROT_NONE would prevent such a mix up: the address space is still there, but not accessible. BTW: Note that even in the current implementation there is a hole available between the regions for /libexec/ld-elf.so.1 itself, starting at 0x800534000 in the above examples. > Please, try this patch instead. > I have tried your patch on my amd64 8.0-CURRENT system and it works perfectly with the described test program. I will stress test it later by running a "make buildworld". In can easily be demonstrated however, that allocations using mmap() as described above may end up in "strange" locations. > diff --git a/libexec/rtld-elf/map_object.c b/libexec/rtld-elf/map_object.c > index 2d06074..3266af0 100644 > --- a/libexec/rtld-elf/map_object.c > +++ b/libexec/rtld-elf/map_object.c > @@ -83,6 +83,7 @@ map_object(int fd, const char *path, const struct stat *sb) > Elf_Addr bss_vaddr; > Elf_Addr bss_vlimit; > caddr_t bss_addr; > + size_t hole; > > hdr = get_elf_header(fd, path); > if (hdr == NULL) > @@ -91,8 +92,7 @@ map_object(int fd, const char *path, const struct stat *sb) > /* > * Scan the program header entries, and save key information. > * > - * We rely on there being exactly two load segments, text and data, > - * in that order. > + * We expect that the loadable segments are ordered by load address. > */ > phdr = (Elf_Phdr *) ((char *)hdr + hdr->e_phoff); > phsize = hdr->e_phnum * sizeof (phdr[0]); > @@ -214,6 +214,17 @@ map_object(int fd, const char *path, const struct stat *sb) > return NULL; > } > } > + > + /* Unmap the region between two non-adjusted ELF segments */ > + if (i < nsegs) { > + hole = trunc_page(segs[i + 1]->p_vaddr) - bss_vlimit; > + if (hole > 0 && munmap(mapbase + bss_vlimit, hole) == -1) { > + _rtld_error("%s: munmap hole failed: %s", path, > + strerror(errno)); > + return NULL; > + } > + } > + > if (phdr_vaddr == 0 && data_offset <= hdr->e_phoff && > (data_vlimit - data_vaddr + data_offset) >= > (hdr->e_phoff + hdr->e_phnum * sizeof (Elf_Phdr))) {