Hi list,
after upgrade on OpenBSD 5.2 we observe the following message from ntpd:

Oct 22 17:20:13 gg74 ntpd[2918]: ntpd 4.2.6p2@1.2194-o Tue Oct 16 20:26:47 UTC 
2012 (1)
Oct 22 17:20:13 gg74 ntpd[10103]: mlockall(): Cannot allocate memory
Oct 22 17:20:13 gg74 ntpd[10103]: signal_no_reset: signal 13 had flags 12
Oct 22 17:20:13 gg74 ntpd[10103]: proto: precision = 6.390 usec
...
This doesn't prevent ntpd from starting, however.
We tried to debug this problem. This occurs when an application tries to set 
resource limits (more precisely, RLIMIT_MEMLOCK) to some relatively low value 
and then does mlockall().

The problem seems to be in uvm_map_pageable_all() function 
(sys/uvm/uvm_map.c). This function is a "special case of uvm_map_pageable", 
which tries to mlockall() all mapped memory regions.
Prior to calling uvm_map_pageable_wire(), which actually does locking, it 
tries to count how many memory bytes will be locked, and compares this number 
with uvmexp.wiredmax, which is set by RLIMIT_MEMLOCK.
The problem is that counting algorithm doesn't take into account that some 
pages have VM_PROT_NONE flag set and hence won't be locked anyway.
Later in uvm_map_pageable_wire() these pages are skipped when doing actual 
job.

Attached patch fixes the problem on OpenBSD 5.2.

I'm also attaching a simple test application, that can be used to reproduce 
the bug. Just compile and run as root (otherwise mlockall() doesn't work 
anyway). On OpenBSD 5.2 the RLIMIT_MEMLOCK limit will be significantly higher 
than on OpenBSD 5.1. After applying my patch they will be almost the same.

Thank you for your attention.

// Ilya
--- /mount/blink/aegis/project/gg/history/os/src/sys/uvm/uvm_map.c	2012/10/17 12:56:27	1.58
+++ /mount/blink/aegis/project/gg/history/os/src/sys/uvm/uvm_map.c	2012/10/24 18:15:06	1.59
@@ -2207,7 +2207,8 @@ uvm_map_pageable_all(struct vm_map *map, int flags, vs
 	 */
 	size = 0;
 	RB_FOREACH(iter, uvm_map_addr, &map->addr) {
-		if (VM_MAPENT_ISWIRED(iter) || UVM_ET_ISHOLE(iter))
+		if (VM_MAPENT_ISWIRED(iter) || UVM_ET_ISHOLE(iter)
+		    || iter->protection == VM_PROT_NONE)
 			continue;
 
 		size += iter->end - iter->start;
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <sys/resource.h>

#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <err.h>

int
main(int argc, char **argv) {

	printf("mlockall() test starting\n");

	struct rlimit rl;
	if (getrlimit(RLIMIT_MEMLOCK, &rl) == -1) {
		err(1, "Cannot get RLIMIT_MEMLOCK");
	}

	printf("Current MLOCK limits: soft=%d, hard=%d\n",
	       rl.rlim_cur, rl.rlim_max);


	uint32_t curlimit;

	for (curlimit = 5140; curlimit < 1024 * 1024 * 1024; curlimit += 100) {
		rl.rlim_cur = rl.rlim_max = curlimit;
		if (setrlimit(RLIMIT_MEMLOCK, &rl) == -1) {
			err(1, "limit=%d. Cannot set RLIMIT_MEMLOCK", curlimit);
		}

		if (mlockall(MCL_CURRENT|MCL_FUTURE) < 0) {
			if (errno != ENOMEM) {
				err(1, "Insufficient privs for mlockall()");
			}
		} else {
			printf("limit=%d, Memory locked OK\n", curlimit);
			break;
		}
	}

	printf("Finished probing limits, last was %d\n", curlimit);
}

Attachment: signature.asc
Description: This is a digitally signed message part.

Reply via email to