svn commit: r311287 - head/lib/libc/x86/sys

Konstantin Belousov kib at FreeBSD.org
Wed Jan 4 16:10:53 UTC 2017


Author: kib
Date: Wed Jan  4 16:10:52 2017
New Revision: 311287
URL: https://svnweb.freebsd.org/changeset/base/311287

Log:
  __vdso_gettc(): be extra careful with /dev/hpet mappings, never unmap
  the mapping which might be accessed by other threads.
  
  If a pointer to the /dev/hpet register page mapping was stored into
  the hpet_dev_map, other threads might access the page at any time.
  Never unmap it, instead, keep track of mappings for all hpet units in
  smal array.  Store pointer to the newly mapped registers page using
  CAS, to detect parallel mappings.
  
  It appeared relatively easy to demonstrate the problem by arranging
  two threads which perform gettimeofday(2) concurently, first time in
  the process address space, when HPET is used for timecounter.
  
  PR:	215715
  Sponsored by:	The FreeBSD Foundation
  MFC after:	1 week

Modified:
  head/lib/libc/x86/sys/__vdso_gettc.c

Modified: head/lib/libc/x86/sys/__vdso_gettc.c
==============================================================================
--- head/lib/libc/x86/sys/__vdso_gettc.c	Wed Jan  4 16:09:45 2017	(r311286)
+++ head/lib/libc/x86/sys/__vdso_gettc.c	Wed Jan  4 16:10:52 2017	(r311287)
@@ -1,6 +1,6 @@
 /*-
  * Copyright (c) 2012 Konstantin Belousov <kib at FreeBSD.org>
- * Copyright (c) 2016 The FreeBSD Foundation
+ * Copyright (c) 2016, 2017 The FreeBSD Foundation
  * All rights reserved.
  *
  * Portions of this software were developed by Konstantin Belousov
@@ -42,11 +42,11 @@ __FBSDID("$FreeBSD$");
 #include <string.h>
 #include <unistd.h>
 #include "un-namespace.h"
+#include <machine/atomic.h>
 #include <machine/cpufunc.h>
 #include <machine/specialreg.h>
 #include <dev/acpica/acpi_hpet.h>
 #ifdef __amd64__
-#include <machine/atomic.h>
 #include <dev/hyperv/hyperv.h>
 #endif
 #include "libc_private.h"
@@ -115,37 +115,47 @@ __vdso_rdtsc32(void)
 	return (rdtsc32());
 }
 
-static char *hpet_dev_map = NULL;
-static uint32_t hpet_idx = 0xffffffff;
+#define	HPET_DEV_MAP_MAX	10
+static volatile char *hpet_dev_map[HPET_DEV_MAP_MAX];
 
 static void
 __vdso_init_hpet(uint32_t u)
 {
 	static const char devprefix[] = "/dev/hpet";
 	char devname[64], *c, *c1, t;
+	volatile char *new_map, *old_map;
+	uint32_t u1;
 	int fd;
 
 	c1 = c = stpcpy(devname, devprefix);
-	u = hpet_idx;
+	u1 = u;
 	do {
-		*c++ = u % 10 + '0';
-		u /= 10;
-	} while (u != 0);
+		*c++ = u1 % 10 + '0';
+		u1 /= 10;
+	} while (u1 != 0);
 	*c = '\0';
 	for (c--; c1 != c; c1++, c--) {
 		t = *c1;
 		*c1 = *c;
 		*c = t;
 	}
+
+	old_map = hpet_dev_map[u];
+	if (old_map != NULL)
+		return;
+
 	fd = _open(devname, O_RDONLY);
 	if (fd == -1) {
-		hpet_dev_map = MAP_FAILED;
+		atomic_cmpset_rel_ptr((volatile uintptr_t *)&hpet_dev_map[u],
+		    (uintptr_t)old_map, (uintptr_t)MAP_FAILED);
 		return;
 	}
-	if (hpet_dev_map != NULL && hpet_dev_map != MAP_FAILED)
-		munmap(hpet_dev_map, PAGE_SIZE);
-	hpet_dev_map = mmap(NULL, PAGE_SIZE, PROT_READ, MAP_SHARED, fd, 0);
+	new_map = mmap(NULL, PAGE_SIZE, PROT_READ, MAP_SHARED, fd, 0);
 	_close(fd);
+	if (atomic_cmpset_rel_ptr((volatile uintptr_t *)&hpet_dev_map[u],
+	    (uintptr_t)old_map, (uintptr_t)new_map) == 0 &&
+	    new_map != MAP_FAILED)
+		munmap((void *)new_map, PAGE_SIZE);
 }
 
 #ifdef __amd64__
@@ -213,7 +223,8 @@ __vdso_hyperv_tsc(struct hyperv_reftsc *
 int
 __vdso_gettc(const struct vdso_timehands *th, u_int *tc)
 {
-	uint32_t tmp;
+	volatile char *map;
+	uint32_t idx;
 
 	switch (th->th_algo) {
 	case VDSO_TH_ALGO_X86_TSC:
@@ -221,14 +232,19 @@ __vdso_gettc(const struct vdso_timehands
 		    __vdso_rdtsc32();
 		return (0);
 	case VDSO_TH_ALGO_X86_HPET:
-		tmp = th->th_x86_hpet_idx;
-		if (hpet_dev_map == NULL || tmp != hpet_idx) {
-			hpet_idx = tmp;
-			__vdso_init_hpet(hpet_idx);
+		idx = th->th_x86_hpet_idx;
+		if (idx >= HPET_DEV_MAP_MAX)
+			return (ENOSYS);
+		map = (volatile char *)atomic_load_acq_ptr(
+		    (volatile uintptr_t *)&hpet_dev_map[idx]);
+		if (map == NULL) {
+			__vdso_init_hpet(idx);
+			map = (volatile char *)atomic_load_acq_ptr(
+			    (volatile uintptr_t *)&hpet_dev_map[idx]);
 		}
-		if (hpet_dev_map == MAP_FAILED)
+		if (map == MAP_FAILED)
 			return (ENOSYS);
-		*tc = *(volatile uint32_t *)(hpet_dev_map + HPET_MAIN_COUNTER);
+		*tc = *(volatile uint32_t *)(map + HPET_MAIN_COUNTER);
 		return (0);
 #ifdef __amd64__
 	case VDSO_TH_ALGO_X86_HVTSC:


More information about the svn-src-head mailing list