Skip to content

Commit

Permalink
8309475: Test java/foreign/TestByteBuffer.java fails: a problem with …
Browse files Browse the repository at this point in the history
…msync (aix)

Reviewed-by: mbaesken, alanb, mdoerr
  • Loading branch information
Tyler Steele committed Aug 18, 2023
1 parent f481477 commit 395fc78
Show file tree
Hide file tree
Showing 3 changed files with 210 additions and 25 deletions.
210 changes: 210 additions & 0 deletions src/java.base/aix/native/libnio/MappedMemoryUtils.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
/*
* Copyright (c) 2001, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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 General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

#include "jni.h"
#include "jni_util.h"
#include "jvm.h"
#include "jlong.h"
#include "java_nio_MappedMemoryUtils.h"
#include <assert.h>
#include <sys/mman.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <sys/procfs.h>
#include <unistd.h>

typedef char mincore_vec_t;

static long calculate_number_of_pages_in_range(void* address, size_t len, size_t pagesize) {
uintptr_t address_unaligned = (uintptr_t) address;
uintptr_t address_aligned = address_unaligned & (~(pagesize - 1));
size_t len2 = len + (address_unaligned - address_aligned);
long numPages = (len2 + pagesize - 1) / pagesize;
return numPages;
}

JNIEXPORT jboolean JNICALL
Java_java_nio_MappedMemoryUtils_isLoaded0(JNIEnv *env, jobject obj, jlong address,
jlong len, jlong numPages)
{
jboolean loaded = JNI_TRUE;
int result = 0;
long i = 0;
void *a = (void *) jlong_to_ptr(address);
mincore_vec_t* vec = NULL;

/* See JDK-8186665 */
size_t pagesize = (size_t)sysconf(_SC_PAGESIZE);
if ((long)pagesize == -1) {
return JNI_FALSE;
}
numPages = (jlong) calculate_number_of_pages_in_range(a, len, pagesize);

/* Include space for one sentinel byte at the end of the buffer
* to catch overflows. */
vec = (mincore_vec_t*) malloc(numPages + 1);

if (vec == NULL) {
JNU_ThrowOutOfMemoryError(env, NULL);
return JNI_FALSE;
}

vec[numPages] = '\x7f'; /* Write sentinel. */
result = mincore(a, (size_t)len, vec);
assert(vec[numPages] == '\x7f'); /* Check sentinel. */

if (result == -1) {
JNU_ThrowIOExceptionWithLastError(env, "mincore failed");
free(vec);
return JNI_FALSE;
}

for (i=0; i<numPages; i++) {
if (vec[i] == 0) {
loaded = JNI_FALSE;
break;
}
}
free(vec);
return loaded;
}


JNIEXPORT void JNICALL
Java_java_nio_MappedMemoryUtils_load0(JNIEnv *env, jobject obj, jlong address,
jlong len)
{
char *a = (char *)jlong_to_ptr(address);
int result = madvise((caddr_t)a, (size_t)len, MADV_WILLNEED);
if (result == -1) {
JNU_ThrowIOExceptionWithMessageAndLastError(env, "madvise with advise MADV_WILLNEED failed");
}
}

JNIEXPORT void JNICALL
Java_java_nio_MappedMemoryUtils_unload0(JNIEnv *env, jobject obj, jlong address,
jlong len)
{
char *a = (char *)jlong_to_ptr(address);
int result = madvise((caddr_t)a, (size_t)len, MADV_DONTNEED);
if (result == -1) {
JNU_ThrowIOExceptionWithMessageAndLastError(env, "madvise with advise MADV_DONTNEED failed");
}
}

static void set_error_if_shared(JNIEnv* env, prmap_t* map_entry)
{
if (map_entry->pr_mflags & MA_SHARED) {
// MA_SHARED => MAP_SHARED => !MAP_PRIVATE. This error is valid and should be thrown.
JNU_ThrowIOExceptionWithMessageAndLastError(env, "msync with parameter MS_SYNC failed (MAP_SHARED)");
return;
} else {
// O.W. MAP_PRIVATE or no flag was specified and EINVAL is the expected behaviour.
return;
}
}

static void check_proc_map_array(JNIEnv* env, FILE* proc_file, prmap_t* map_entry, void* end_address)
{
while (!feof(proc_file)) {
memset(map_entry, '\0', sizeof(prmap_t));
fread((char*)map_entry, sizeof(prmap_t), 1, proc_file);
if (ferror(proc_file)) {
JNU_ThrowIOExceptionWithMessageAndLastError(env,
"msync with parameter MS_SYNC failed (could not read /proc/<pid>/map)");
return;
} else if (map_entry->pr_vaddr <= end_address &&
(uint64_t)end_address <= (uint64_t)map_entry->pr_vaddr + map_entry->pr_size) {
set_error_if_shared(env, map_entry);
return;
}
}
JNU_ThrowIOExceptionWithMessageAndLastError(env,
"msync with parameter MS_SYNC failed (address not found)");
}

// '/proc/' + <pid> + '/map' + '\0'
#define PFNAME_LEN 32
static void check_aix_einval(JNIEnv* env, void* end_address)
{
// If EINVAL is set for a mmap address on AIX, additional validation is required.
// AIX will set EINVAL when msync is called on a mmap address that didn't receive MAP_SHARED
// as a flag (since MAP_PRIVATE is the default).
// https://www.ibm.com/docs/en/aix/7.2?topic=m-msync-subroutine

FILE* proc_file;
{
char* fname = (char*) malloc(sizeof(char) * PFNAME_LEN);
pid_t the_pid = getpid();
jio_snprintf(fname, PFNAME_LEN, "/proc/%d/map", the_pid);
proc_file = fopen(fname, "r");
free(fname);
}
if (!proc_file) {
JNU_ThrowIOExceptionWithMessageAndLastError(env,
"msync with parameter MS_SYNC failed (could not open /proc/<pid>/map)");
return;
}
{
prmap_t* map_entry = (prmap_t*) malloc(sizeof(prmap_t));
check_proc_map_array(env, proc_file, map_entry, end_address);
free(map_entry);
}
fclose(proc_file);
}

// Normally we would just let msync handle this, but since we'll be (potentially) ignoring
// the error code returned by msync, we check the args before the call instead.
static int validate_msync_address(size_t address)
{
size_t pagesize = (size_t)sysconf(_SC_PAGESIZE);
if (address % pagesize != 0) {
errno = EINVAL;
return -1;
}
return 0;
}

JNIEXPORT void JNICALL
Java_java_nio_MappedMemoryUtils_force0(JNIEnv *env, jobject obj, jobject fdo,
jlong address, jlong len)
{
void* a = (void *)jlong_to_ptr(address);
if (validate_msync_address((size_t)a) > 0) {
JNU_ThrowIOExceptionWithMessageAndLastError(env,
"msync with parameter MS_SYNC failed (arguments invalid)");
return;
}
int result = msync(a, (size_t)len, MS_SYNC);
if (result == -1) {
void* end_address = (void*)jlong_to_ptr(address + len);
if (errno == EINVAL) {
check_aix_einval(env, end_address);
return;
}
JNU_ThrowIOExceptionWithMessageAndLastError(env, "msync with parameter MS_SYNC failed");
}
}
23 changes: 0 additions & 23 deletions src/java.base/unix/native/libnio/MappedMemoryUtils.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,27 +33,13 @@
#include <stddef.h>
#include <stdlib.h>

#ifdef _AIX
#include <unistd.h>
#endif

/* Output type for mincore(2) */
#ifdef __linux__
typedef unsigned char mincore_vec_t;
#else
typedef char mincore_vec_t;
#endif

#ifdef _AIX
static long calculate_number_of_pages_in_range(void* address, size_t len, size_t pagesize) {
uintptr_t address_unaligned = (uintptr_t) address;
uintptr_t address_aligned = address_unaligned & (~(pagesize - 1));
size_t len2 = len + (address_unaligned - address_aligned);
long numPages = (len2 + pagesize - 1) / pagesize;
return numPages;
}
#endif

JNIEXPORT jboolean JNICALL
Java_java_nio_MappedMemoryUtils_isLoaded0(JNIEnv *env, jobject obj, jlong address,
jlong len, jlong numPages)
Expand All @@ -64,15 +50,6 @@ Java_java_nio_MappedMemoryUtils_isLoaded0(JNIEnv *env, jobject obj, jlong addres
void *a = (void *) jlong_to_ptr(address);
mincore_vec_t* vec = NULL;

#ifdef _AIX
/* See JDK-8186665 */
size_t pagesize = (size_t)sysconf(_SC_PAGESIZE);
if ((long)pagesize == -1) {
return JNI_FALSE;
}
numPages = (jlong) calculate_number_of_pages_in_range(a, len, pagesize);
#endif

/* Include space for one sentinel byte at the end of the buffer
* to catch overflows. */
vec = (mincore_vec_t*) malloc(numPages + 1);
Expand Down
2 changes: 0 additions & 2 deletions test/jdk/ProblemList.txt
Original file line number Diff line number Diff line change
Expand Up @@ -762,8 +762,6 @@ jdk/jfr/api/consumer/recordingstream/TestOnEvent.java 8255404 linux-x6

# jdk_foreign

java/foreign/TestByteBuffer.java 8309475 aix-ppc64

############################################################################
# Client manual tests

Expand Down

3 comments on commit 395fc78

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@backwaterred
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/backport jdk21u

@openjdk
Copy link

@openjdk openjdk bot commented on 395fc78 Aug 22, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@backwaterred the backport was successfully created on the branch backwaterred-backport-395fc788 in my personal fork of openjdk/jdk21u. To create a pull request with this backport targeting openjdk/jdk21u:master, just click the following link:

➡️ Create pull request

The title of the pull request is automatically filled in correctly and below you find a suggestion for the pull request body:

Hi all,

This pull request contains a backport of commit 395fc788 from the openjdk/jdk repository.

The commit being backported was authored by Tyler Steele on 18 Aug 2023 and was reviewed by Matthias Baesken, Alan Bateman and Martin Doerr.

Thanks!

If you need to update the source branch of the pull then run the following commands in a local clone of your personal fork of openjdk/jdk21u:

$ git fetch https://github.com/openjdk-bots/jdk21u.git backwaterred-backport-395fc788:backwaterred-backport-395fc788
$ git checkout backwaterred-backport-395fc788
# make changes
$ git add paths/to/changed/files
$ git commit --message 'Describe additional changes made'
$ git push https://github.com/openjdk-bots/jdk21u.git backwaterred-backport-395fc788

Please sign in to comment.