Skip to content

Commit 2408df2

Browse files
Tyler SteeleTheRealMDoerr
authored andcommitted
8309475: Test java/foreign/TestByteBuffer.java fails: a problem with msync (aix)
Backport-of: 395fc78880a91eeb06a08c1abf6905d580a650c0
1 parent 1f5ba7a commit 2408df2

File tree

3 files changed

+210
-25
lines changed

3 files changed

+210
-25
lines changed
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
/*
2+
* Copyright (c) 2001, 2023, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
26+
#include "jni.h"
27+
#include "jni_util.h"
28+
#include "jvm.h"
29+
#include "jlong.h"
30+
#include "java_nio_MappedMemoryUtils.h"
31+
#include <assert.h>
32+
#include <sys/mman.h>
33+
#include <stddef.h>
34+
#include <stdlib.h>
35+
#include <string.h>
36+
#include <sys/procfs.h>
37+
#include <unistd.h>
38+
39+
typedef char mincore_vec_t;
40+
41+
static long calculate_number_of_pages_in_range(void* address, size_t len, size_t pagesize) {
42+
uintptr_t address_unaligned = (uintptr_t) address;
43+
uintptr_t address_aligned = address_unaligned & (~(pagesize - 1));
44+
size_t len2 = len + (address_unaligned - address_aligned);
45+
long numPages = (len2 + pagesize - 1) / pagesize;
46+
return numPages;
47+
}
48+
49+
JNIEXPORT jboolean JNICALL
50+
Java_java_nio_MappedMemoryUtils_isLoaded0(JNIEnv *env, jobject obj, jlong address,
51+
jlong len, jlong numPages)
52+
{
53+
jboolean loaded = JNI_TRUE;
54+
int result = 0;
55+
long i = 0;
56+
void *a = (void *) jlong_to_ptr(address);
57+
mincore_vec_t* vec = NULL;
58+
59+
/* See JDK-8186665 */
60+
size_t pagesize = (size_t)sysconf(_SC_PAGESIZE);
61+
if ((long)pagesize == -1) {
62+
return JNI_FALSE;
63+
}
64+
numPages = (jlong) calculate_number_of_pages_in_range(a, len, pagesize);
65+
66+
/* Include space for one sentinel byte at the end of the buffer
67+
* to catch overflows. */
68+
vec = (mincore_vec_t*) malloc(numPages + 1);
69+
70+
if (vec == NULL) {
71+
JNU_ThrowOutOfMemoryError(env, NULL);
72+
return JNI_FALSE;
73+
}
74+
75+
vec[numPages] = '\x7f'; /* Write sentinel. */
76+
result = mincore(a, (size_t)len, vec);
77+
assert(vec[numPages] == '\x7f'); /* Check sentinel. */
78+
79+
if (result == -1) {
80+
JNU_ThrowIOExceptionWithLastError(env, "mincore failed");
81+
free(vec);
82+
return JNI_FALSE;
83+
}
84+
85+
for (i=0; i<numPages; i++) {
86+
if (vec[i] == 0) {
87+
loaded = JNI_FALSE;
88+
break;
89+
}
90+
}
91+
free(vec);
92+
return loaded;
93+
}
94+
95+
96+
JNIEXPORT void JNICALL
97+
Java_java_nio_MappedMemoryUtils_load0(JNIEnv *env, jobject obj, jlong address,
98+
jlong len)
99+
{
100+
char *a = (char *)jlong_to_ptr(address);
101+
int result = madvise((caddr_t)a, (size_t)len, MADV_WILLNEED);
102+
if (result == -1) {
103+
JNU_ThrowIOExceptionWithMessageAndLastError(env, "madvise with advise MADV_WILLNEED failed");
104+
}
105+
}
106+
107+
JNIEXPORT void JNICALL
108+
Java_java_nio_MappedMemoryUtils_unload0(JNIEnv *env, jobject obj, jlong address,
109+
jlong len)
110+
{
111+
char *a = (char *)jlong_to_ptr(address);
112+
int result = madvise((caddr_t)a, (size_t)len, MADV_DONTNEED);
113+
if (result == -1) {
114+
JNU_ThrowIOExceptionWithMessageAndLastError(env, "madvise with advise MADV_DONTNEED failed");
115+
}
116+
}
117+
118+
static void set_error_if_shared(JNIEnv* env, prmap_t* map_entry)
119+
{
120+
if (map_entry->pr_mflags & MA_SHARED) {
121+
// MA_SHARED => MAP_SHARED => !MAP_PRIVATE. This error is valid and should be thrown.
122+
JNU_ThrowIOExceptionWithMessageAndLastError(env, "msync with parameter MS_SYNC failed (MAP_SHARED)");
123+
return;
124+
} else {
125+
// O.W. MAP_PRIVATE or no flag was specified and EINVAL is the expected behaviour.
126+
return;
127+
}
128+
}
129+
130+
static void check_proc_map_array(JNIEnv* env, FILE* proc_file, prmap_t* map_entry, void* end_address)
131+
{
132+
while (!feof(proc_file)) {
133+
memset(map_entry, '\0', sizeof(prmap_t));
134+
fread((char*)map_entry, sizeof(prmap_t), 1, proc_file);
135+
if (ferror(proc_file)) {
136+
JNU_ThrowIOExceptionWithMessageAndLastError(env,
137+
"msync with parameter MS_SYNC failed (could not read /proc/<pid>/map)");
138+
return;
139+
} else if (map_entry->pr_vaddr <= end_address &&
140+
(uint64_t)end_address <= (uint64_t)map_entry->pr_vaddr + map_entry->pr_size) {
141+
set_error_if_shared(env, map_entry);
142+
return;
143+
}
144+
}
145+
JNU_ThrowIOExceptionWithMessageAndLastError(env,
146+
"msync with parameter MS_SYNC failed (address not found)");
147+
}
148+
149+
// '/proc/' + <pid> + '/map' + '\0'
150+
#define PFNAME_LEN 32
151+
static void check_aix_einval(JNIEnv* env, void* end_address)
152+
{
153+
// If EINVAL is set for a mmap address on AIX, additional validation is required.
154+
// AIX will set EINVAL when msync is called on a mmap address that didn't receive MAP_SHARED
155+
// as a flag (since MAP_PRIVATE is the default).
156+
// https://www.ibm.com/docs/en/aix/7.2?topic=m-msync-subroutine
157+
158+
FILE* proc_file;
159+
{
160+
char* fname = (char*) malloc(sizeof(char) * PFNAME_LEN);
161+
pid_t the_pid = getpid();
162+
jio_snprintf(fname, PFNAME_LEN, "/proc/%d/map", the_pid);
163+
proc_file = fopen(fname, "r");
164+
free(fname);
165+
}
166+
if (!proc_file) {
167+
JNU_ThrowIOExceptionWithMessageAndLastError(env,
168+
"msync with parameter MS_SYNC failed (could not open /proc/<pid>/map)");
169+
return;
170+
}
171+
{
172+
prmap_t* map_entry = (prmap_t*) malloc(sizeof(prmap_t));
173+
check_proc_map_array(env, proc_file, map_entry, end_address);
174+
free(map_entry);
175+
}
176+
fclose(proc_file);
177+
}
178+
179+
// Normally we would just let msync handle this, but since we'll be (potentially) ignoring
180+
// the error code returned by msync, we check the args before the call instead.
181+
static int validate_msync_address(size_t address)
182+
{
183+
size_t pagesize = (size_t)sysconf(_SC_PAGESIZE);
184+
if (address % pagesize != 0) {
185+
errno = EINVAL;
186+
return -1;
187+
}
188+
return 0;
189+
}
190+
191+
JNIEXPORT void JNICALL
192+
Java_java_nio_MappedMemoryUtils_force0(JNIEnv *env, jobject obj, jobject fdo,
193+
jlong address, jlong len)
194+
{
195+
void* a = (void *)jlong_to_ptr(address);
196+
if (validate_msync_address((size_t)a) > 0) {
197+
JNU_ThrowIOExceptionWithMessageAndLastError(env,
198+
"msync with parameter MS_SYNC failed (arguments invalid)");
199+
return;
200+
}
201+
int result = msync(a, (size_t)len, MS_SYNC);
202+
if (result == -1) {
203+
void* end_address = (void*)jlong_to_ptr(address + len);
204+
if (errno == EINVAL) {
205+
check_aix_einval(env, end_address);
206+
return;
207+
}
208+
JNU_ThrowIOExceptionWithMessageAndLastError(env, "msync with parameter MS_SYNC failed");
209+
}
210+
}

src/java.base/unix/native/libnio/MappedMemoryUtils.c

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -33,27 +33,13 @@
3333
#include <stddef.h>
3434
#include <stdlib.h>
3535

36-
#ifdef _AIX
37-
#include <unistd.h>
38-
#endif
39-
4036
/* Output type for mincore(2) */
4137
#ifdef __linux__
4238
typedef unsigned char mincore_vec_t;
4339
#else
4440
typedef char mincore_vec_t;
4541
#endif
4642

47-
#ifdef _AIX
48-
static long calculate_number_of_pages_in_range(void* address, size_t len, size_t pagesize) {
49-
uintptr_t address_unaligned = (uintptr_t) address;
50-
uintptr_t address_aligned = address_unaligned & (~(pagesize - 1));
51-
size_t len2 = len + (address_unaligned - address_aligned);
52-
long numPages = (len2 + pagesize - 1) / pagesize;
53-
return numPages;
54-
}
55-
#endif
56-
5743
JNIEXPORT jboolean JNICALL
5844
Java_java_nio_MappedMemoryUtils_isLoaded0(JNIEnv *env, jobject obj, jlong address,
5945
jlong len, jlong numPages)
@@ -64,15 +50,6 @@ Java_java_nio_MappedMemoryUtils_isLoaded0(JNIEnv *env, jobject obj, jlong addres
6450
void *a = (void *) jlong_to_ptr(address);
6551
mincore_vec_t* vec = NULL;
6652

67-
#ifdef _AIX
68-
/* See JDK-8186665 */
69-
size_t pagesize = (size_t)sysconf(_SC_PAGESIZE);
70-
if ((long)pagesize == -1) {
71-
return JNI_FALSE;
72-
}
73-
numPages = (jlong) calculate_number_of_pages_in_range(a, len, pagesize);
74-
#endif
75-
7653
/* Include space for one sentinel byte at the end of the buffer
7754
* to catch overflows. */
7855
vec = (mincore_vec_t*) malloc(numPages + 1);

test/jdk/ProblemList.txt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -765,8 +765,6 @@ jdk/jfr/tool/TestView.java 8309238 macosx-x
765765

766766
# jdk_foreign
767767

768-
java/foreign/TestByteBuffer.java 8309475 aix-ppc64
769-
770768
############################################################################
771769
# Client manual tests
772770

0 commit comments

Comments
 (0)