Commit 127555c6 authored by Ivo Anjo's avatar Ivo Anjo

Make javartm usage safe on non-rtm cpus

One of the use cases of javartm is that any app may use it, and use the Transaction.RTM_AVAILABLE
flag to trigger execution with javartm, or to decide how to fallback onto something else.

Nevertheless, if the application ignored the RTM_AVAILABLE flag and mistakenly invoked the javartm
functions, it would result in a VM segfault.

To avoid this, the native code part of javartm has been split into three libraries. The first is
libtestrtmsupport.so, that only includes the code that checks for rtm usage.
After checking for rtm support, the code decides if either libjavartm-dummy.so or libjavartm.so
are loaded; libjavartm-dummy.so just includes dummy implementations of the native code functions
that throw exceptions whenever they are used.

This way, older machines can never trigger the execution of rtm instructions on older machines,
making javartm usage safe.
parent d7edb0d6
......@@ -44,11 +44,42 @@
<path path="${classes.dir}"/>
</classpath>
<class name="javartm.Transaction"/>
<class name="javartm.TestRtmSupport"/>
</javah>
</target>
<!-- cpptasks support -->
<target name="jni-compile" depends="jni-headers">
<target name="jni-compile-libtestrtmsupport" depends="jni-headers">
<taskdef resource="cpptasks.tasks"/>
<mkdir dir="${jnioutput.dir}"/>
<cc outtype="shared" subsystem="console" objdir="${jni.dir}" outfile="${jnioutput.dir}/testrtmsupport">
<compilerarg value="-Wall"/>
<compilerarg value="-O2"/>
<compilerarg value="-std=c99"/>
<fileset dir="${c.src.dir}" includes="**/TestRtmSupport.c"/>
<includepath>
<path path="${java.home}/../include"/>
<path path="${jni.dir}"/>
</includepath>
</cc>
</target>
<target name="jni-compile-libjavartm-dummy" depends="jni-headers">
<taskdef resource="cpptasks.tasks"/>
<mkdir dir="${jnioutput.dir}"/>
<cc outtype="shared" subsystem="console" objdir="${jni.dir}" outfile="${jnioutput.dir}/javartm-dummy">
<compilerarg value="-Wall"/>
<compilerarg value="-O2"/>
<compilerarg value="-std=c99"/>
<fileset dir="${c.src.dir}" includes="**/Transaction-dummy.c"/>
<includepath>
<path path="${java.home}/../include"/>
<path path="${jni.dir}"/>
</includepath>
</cc>
</target>
<target name="jni-compile-libjavartm" depends="jni-headers">
<taskdef resource="cpptasks.tasks"/>
<mkdir dir="${jnioutput.dir}"/>
<cc outtype="shared" subsystem="console" objdir="${jni.dir}" outfile="${jnilib.file}">
......@@ -58,7 +89,9 @@
<compilerarg value="-mrtm"/>
<!-- we are going to be running on haswell at least, so why not take advantage of it -->
<compilerarg value="-march=core-avx2"/>
<fileset dir="${c.src.dir}" includes="**/*.c"/>
<fileset dir="${c.src.dir}"
includes="**/*.c"
excludes="**/TestRtmSupport.c **/Transaction-dummy.c"/>
<includepath>
<path path="${java.home}/../include"/>
<path path="${jni.dir}"/>
......@@ -66,12 +99,12 @@
</cc>
</target>
<target name="oldgcc-jni-compile" depends="jni-headers">
<target name="oldgcc-jni-compile-javartm" depends="jni-headers">
<taskdef resource="cpptasks.tasks"/>
<mkdir dir="${jnioutput.dir}"/>
<cc outtype="shared" subsystem="console" objdir="${jni.dir}" outfile="${jnilib.file}">
<compilerarg value="-Wall"/>
<compilerarg value="-std=c99"/>
<compilerarg value="-std=gnu99"/>
<compilerarg value="-O2"/>
<!--
-march=core-avx-i is a bit less recent than -march=core-avx2, for older compilers;
......@@ -79,7 +112,9 @@
-->
<compilerarg value="-march=core-avx-i"/>
<defineset define="JAVARTM_RTM_INTRINSICS"/>
<fileset dir="${c.src.dir}" includes="**/*.c"/>
<fileset dir="${c.src.dir}"
includes="**/*.c"
excludes="**/TestRtmSupport.c **/Transaction-dummy.c"/>
<includepath>
<path path="${java.home}/../include"/>
<path path="${jni.dir}"/>
......@@ -87,6 +122,9 @@
</cc>
</target>
<target name="jni-compile" depends="jni-compile-libtestrtmsupport,jni-compile-libjavartm-dummy,jni-compile-libjavartm"/>
<target name="oldgcc-jni-compile" depends="jni-compile-libtestrtmsupport,jni-compile-libjavartm-dummy,oldgcc-jni-compile-javartm"/>
<target name="build" depends="clean,jni-compile" description="Builds javartm (java and native resources)"/>
<target name="oldgcc-build" depends="clean,oldgcc-jni-compile" description="Builds javartm (java and native resources) [GCC &lt; 4.8]"/>
......
/*
* javartm: a Java library for Restricted Transactional Memory
* Copyright (C) 2013 Ivo Anjo <ivo.anjo@ist.utl.pt>
*
* This file is part of javartm.
*
* javartm is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* javartm is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with javartm. If not, see <http://www.gnu.org/licenses/>.
*/
// Small test library, allowing rtm support to be tested without loading the rest of the rtm code
// Used to test cpuid support
#include <x86intrin.h>
#include <cpuid.h>
// Needed for JNI
#include <jni.h>
#include "javartm_TestRtmSupport.h"
#define bit_RTM (1 << 11)
JNIEXPORT jboolean JNICALL Java_javartm_TestRtmSupport_rtmAvailable(JNIEnv *env, jclass cls) {
unsigned int eax, ebx, ecx, edx;
if (__get_cpuid_max(0, NULL) >= 7) {
__cpuid_count(7, 0, eax, ebx, ecx, edx);
if (ebx & bit_RTM) return 1;
}
return 0;
}
/*
* javartm: a Java library for Restricted Transactional Memory
* Copyright (C) 2013 Ivo Anjo <ivo.anjo@ist.utl.pt>
*
* This file is part of javartm.
*
* javartm is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* javartm is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with javartm. If not, see <http://www.gnu.org/licenses/>.
*/
// Dummy versions of javartm native methods that are used for machines without rtm support
// Doing this avoids having extra tests in the normal lib, which are bad for methods that are
// supposed to run with active transactions
// Needed for JNI
#include <jni.h>
#include "javartm_Transaction.h"
// Other stuff
#include <stdio.h>
void throwException(JNIEnv *env) {
jclass excClass = (*env)->FindClass(env, "java/lang/RuntimeException");
if (!excClass) return;
(*env)->ThrowNew(env, excClass, "No hardware RTM support is available on this machine");
}
JNIEXPORT jboolean JNICALL Java_javartm_Transaction_inTransaction(JNIEnv *env, jclass cls) {
throwException(env);
return 0;
}
JNIEXPORT jint JNICALL Java_javartm_Transaction_begin(JNIEnv *env, jclass cls) {
throwException(env);
return 0;
}
JNIEXPORT void JNICALL Java_javartm_Transaction_commit(JNIEnv *env, jclass cls) {
throwException(env);
}
JNIEXPORT void JNICALL Java_javartm_Transaction_abort__(JNIEnv *env, jclass cls) {
throwException(env);
}
JNIEXPORT void JNICALL Java_javartm_Transaction_abort__J(JNIEnv *env, jclass cls, jlong reason) {
throwException(env);
}
......@@ -37,10 +37,6 @@
#endif
#endif
// Used to test cpuid support
#include <x86intrin.h>
#include <cpuid.h>
// Support for x86 pause instruction
#include <xmmintrin.h>
......@@ -75,15 +71,6 @@ int logMessage(JNIEnv *env, char *level, char *message) {
// ----------------------------------------------------------------------------
JNIEXPORT jboolean JNICALL Java_javartm_Transaction_rtmAvailable(JNIEnv *env, jclass cls) {
unsigned int eax, ebx, ecx, edx;
if (__get_cpuid_max(0, NULL) >= 7) {
__cpuid_count(7, 0, eax, ebx, ecx, edx);
if (ebx & bit_RTM) return 1;
}
return 0;
}
JNIEXPORT jboolean JNICALL Java_javartm_Transaction_inTransaction(JNIEnv *env, jclass cls) {
return _xtest();
}
......
......@@ -25,6 +25,18 @@ import java.io.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
final class TestRtmSupport {
private static final Logger Log = LoggerFactory.getLogger(TestRtmSupport.class);
static {
// Bind native methods
Transaction.loadNativeLibrary("testrtmsupport");
}
private TestRtmSupport() { }
protected native static boolean rtmAvailable();
}
public final class Transaction {
private static final Logger Log = LoggerFactory.getLogger(Transaction.class);
......@@ -41,17 +53,27 @@ public final class Transaction {
public static final int ABORT_DEBUG = 1 << 4;
public static final int ABORT_NESTED = 1 << 5;
public static final boolean RTM_AVAILABLE;
public static final boolean RTM_AVAILABLE = TestRtmSupport.rtmAvailable();
static {
if (!RTM_AVAILABLE) {
Log.info("RTM not supported by current CPU. Loading dummy library.");
loadNativeLibrary("javartm-dummy");
} else {
loadNativeLibrary("javartm");
warmup();
}
}
protected static void loadNativeLibrary(String libraryName) {
// Attempt to load native library from jar
InputStream libFile = Transaction.class.getResourceAsStream("libjavartm.so");
InputStream libFile = Transaction.class.getResourceAsStream("lib" + libraryName + ".so");
if (libFile != null) {
try {
// Native libraries *have* to be loaded from a file, so we
// create a temporary file, dump the native library from the
// jar, and then load it from there
File f = File.createTempFile("libjavartm", "so");
File f = File.createTempFile("lib" + libraryName, "so");
f.deleteOnExit();
FileOutputStream fos = new FileOutputStream(f);
int read;
......@@ -65,42 +87,35 @@ public final class Transaction {
Log.warn("Exception trying to load native library", e);
}
} else {
// Embedded libjavartm.so not found, trying to load directly
System.loadLibrary("javartm");
// Embedded library not found, trying to load directly
System.loadLibrary(libraryName);
}
}
RTM_AVAILABLE = rtmAvailable();
if (!RTM_AVAILABLE) {
Log.warn("RTM not supported by current CPU. Attempting to use it may lead to JVM crashes");
} else {
// Warmup methods
// This is important for two reasons:
// 1) Hotspot uses lazy dynamic linking, and otherwise we could be triggering dynamic
// linking during a transaction. This can be seen by using -verbose:jni to enable
// logging of dynamic linking operations.
// 2) By default, hotspot JIT-recompiles code at around 10k iterations, and trying
// to JIT during a transaction can cause it to keep aborting.
Log.trace("Warming up native methods");
for (int i = 0; i < HOTSPOT_JIT_THRESHOLD * 1.1; i++) {
inTransaction();
begin();
// the abort on the next line makes sure no transaction stays active after the begin
// above, even if the transactional buffer has a bigger capacity than usual
try { abort(); throw new Error("Should never happen"); }
catch (IllegalStateException expected) { }
try { abort(0); throw new Error("Should never happen"); }
catch (IllegalStateException expected) { }
try { commit(); throw new Error("Should never happen"); }
catch (IllegalStateException expected) { }
}
private static void warmup() {
// Warmup methods
// This is important for two reasons:
// 1) Hotspot uses lazy dynamic linking, and otherwise we could be triggering dynamic
// linking during a transaction. This can be seen by using -verbose:jni to enable
// logging of dynamic linking operations.
// 2) By default, hotspot JIT-recompiles code at around 10k iterations, and trying
// to JIT during a transaction can cause it to keep aborting.
Log.trace("Warming up methods");
for (int i = 0; i < HOTSPOT_JIT_THRESHOLD * 1.1; i++) {
inTransaction();
begin();
// the abort on the next line makes sure no transaction stays active after the begin
// above, even if the transactional buffer has a bigger capacity than usual
try { abort(); throw new Error("Should never happen"); }
catch (IllegalStateException expected) { }
try { abort(0); throw new Error("Should never happen"); }
catch (IllegalStateException expected) { }
try { commit(); throw new Error("Should never happen"); }
catch (IllegalStateException expected) { }
}
}
/**
* Test CPU for RTM support.
*/
private native static boolean rtmAvailable();
private Transaction() { }
public native static boolean inTransaction();
public native static int begin();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment