Fix msync() flags on FreeBSD

This commit is contained in:
Justine Tunney 2023-08-21 04:15:05 -07:00
parent ebf784d4f5
commit fffcd98b0e
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
7 changed files with 95 additions and 15 deletions

View file

@ -25,6 +25,7 @@
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/intrin/strace.internal.h" #include "libc/intrin/strace.internal.h"
#include "libc/macros.internal.h" #include "libc/macros.internal.h"
#include "libc/sysv/errfuns.h"
/** /**
* Synchronize memory mapping changes to disk. * Synchronize memory mapping changes to disk.
@ -37,20 +38,61 @@
* @return 0 on success or -1 w/ errno * @return 0 on success or -1 w/ errno
* @raise ECANCELED if thread was cancelled in masked mode * @raise ECANCELED if thread was cancelled in masked mode
* @raise EINTR if we needed to block and a signal was delivered instead * @raise EINTR if we needed to block and a signal was delivered instead
* @raise EINVAL if `MS_SYNC` and `MS_ASYNC` were both specified
* @raise EINVAL if unknown `flags` were passed
* @cancellationpoint * @cancellationpoint
*/ */
int msync(void *addr, size_t size, int flags) { int msync(void *addr, size_t size, int flags) {
int rc; int rc;
BEGIN_CANCELLATION_POINT;
unassert(((flags & MS_SYNC) ^ (flags & MS_ASYNC)) || !(MS_SYNC && MS_ASYNC)); if ((flags & ~(MS_SYNC | MS_ASYNC | MS_INVALIDATE)) ||
if (!IsWindows()) { (flags & (MS_SYNC | MS_ASYNC)) == (MS_SYNC | MS_ASYNC)) {
rc = sys_msync(addr, size, flags); rc = einval();
} else { goto Finished;
rc = sys_msync_nt(addr, size, flags);
} }
// According to POSIX, either MS_SYNC or MS_ASYNC must be specified
// in flags, and indeed failure to include one of these flags will
// cause msync() to fail on some systems. However, Linux permits a
// call to msync() that specifies neither of these flags, with
// semantics that are (currently) equivalent to specifying MS_ASYNC.
// ──Quoth msync(2) of Linux Programmer's Manual
int sysflags = flags;
sysflags = flags;
if (flags & MS_ASYNC) {
sysflags = MS_ASYNC;
} else if (flags & MS_SYNC) {
sysflags = MS_SYNC;
} else {
sysflags = MS_ASYNC;
}
if (flags & MS_INVALIDATE) {
sysflags |= MS_INVALIDATE;
}
// FreeBSD's manual says "The flags argument was both MS_ASYNC and
// MS_INVALIDATE. Only one of these flags is allowed." which makes
// following the POSIX recommendation somewhat difficult.
if (IsFreebsd()) {
if (sysflags == (MS_ASYNC | MS_INVALIDATE)) {
sysflags = MS_INVALIDATE;
}
}
// FreeBSD specifies MS_SYNC as 0 so we shift the Cosmo constants
if (IsFreebsd()) {
sysflags >>= 1;
}
BEGIN_CANCELLATION_POINT;
if (!IsWindows()) {
rc = sys_msync(addr, size, sysflags);
} else {
rc = sys_msync_nt(addr, size, sysflags);
}
END_CANCELLATION_POINT; END_CANCELLATION_POINT;
Finished:
STRACE("msync(%p, %'zu, %#x) → %d% m", addr, size, flags, rc); STRACE("msync(%p, %'zu, %#x) → %d% m", addr, size, flags, rc);
return rc; return rc;
} }

View file

@ -959,9 +959,9 @@ syscon pf PF_X25 9 9 0 0 0 0 0 0
# msync() flags # msync() flags
# #
# group name GNU/Systemd GNU/Systemd (Aarch64) XNU's Not UNIX! MacOS (Arm64) FreeBSD OpenBSD NetBSD The New Technology Commentary # group name GNU/Systemd GNU/Systemd (Aarch64) XNU's Not UNIX! MacOS (Arm64) FreeBSD OpenBSD NetBSD The New Technology Commentary
syscon ms MS_SYNC 4 4 16 16 0 2 4 4 # faked nt syscon ms MS_SYNC 4 4 16 16 1 2 4 4 # faked nt; actually 0 on freebsd
syscon ms MS_ASYNC 1 1 1 1 1 1 1 1 # consensus (faked nt) syscon ms MS_ASYNC 1 1 1 1 2 1 1 1 # faked nt; actually 1 on freebsd
syscon ms MS_INVALIDATE 2 2 2 2 2 4 2 0 syscon ms MS_INVALIDATE 2 2 2 2 4 4 2 0 # actually 2 on freebsd
# statfs() flags # statfs() flags
# #

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h" #include "libc/sysv/consts/syscon.internal.h"
.syscon ms,MS_ASYNC,1,1,1,1,1,1,1,1 .syscon ms,MS_ASYNC,1,1,1,1,2,1,1,1

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h" #include "libc/sysv/consts/syscon.internal.h"
.syscon ms,MS_INVALIDATE,2,2,2,2,2,4,2,0 .syscon ms,MS_INVALIDATE,2,2,2,2,4,4,2,0

View file

@ -1,2 +1,2 @@
#include "libc/sysv/consts/syscon.internal.h" #include "libc/sysv/consts/syscon.internal.h"
.syscon ms,MS_SYNC,4,4,16,16,0,2,4,4 .syscon ms,MS_SYNC,4,4,16,16,1,2,4,4

View file

@ -7,11 +7,10 @@ extern const int MS_SYNC;
extern const int MS_ASYNC; extern const int MS_ASYNC;
extern const int MS_INVALIDATE; extern const int MS_INVALIDATE;
#define MS_ASYNC 1
#define MS_SYNC MS_SYNC #define MS_SYNC MS_SYNC
#define MS_ASYNC MS_ASYNC
#define MS_INVALIDATE MS_INVALIDATE #define MS_INVALIDATE MS_INVALIDATE
COSMOPOLITAN_C_END_ COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_MSYNC_H_ */ #endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_MSYNC_H_ */

View file

@ -16,13 +16,17 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/sysv/consts/msync.h"
#include "libc/atomic.h"
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/intrin/kprintf.h"
#include "libc/runtime/runtime.h" #include "libc/runtime/runtime.h"
#include "libc/sysv/consts/map.h" #include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/msync.h"
#include "libc/sysv/consts/prot.h" #include "libc/sysv/consts/prot.h"
#include "libc/testlib/testlib.h" #include "libc/testlib/testlib.h"
// msync() is a difficult function to test because it doesn't do much
TEST(msync, changeFileMappingAndExit) { TEST(msync, changeFileMappingAndExit) {
int ws; int ws;
char byte; char byte;
@ -33,6 +37,7 @@ TEST(msync, changeFileMappingAndExit) {
(map = mmap(0, 4, PROT_READ | PROT_WRITE, MAP_SHARED, 3, 0))); (map = mmap(0, 4, PROT_READ | PROT_WRITE, MAP_SHARED, 3, 0)));
if (!fork()) { if (!fork()) {
map[0] = 1; map[0] = 1;
// commenting this out will fail on openbsd
ASSERT_SYS(0, 0, msync(map, 4, MS_SYNC)); ASSERT_SYS(0, 0, msync(map, 4, MS_SYNC));
_Exit(123); _Exit(123);
} }
@ -45,3 +50,37 @@ TEST(msync, changeFileMappingAndExit) {
ASSERT_SYS(0, 0, munmap(map, 4)); ASSERT_SYS(0, 0, munmap(map, 4));
ASSERT_SYS(0, 0, close(3)); ASSERT_SYS(0, 0, close(3));
} }
TEST(msync, changeFileMappingAndWakeSpinLockWaiter) {
int ws;
char byte;
char *map;
atomic_char *sem = _mapshared(1);
ASSERT_SYS(0, 3, tmpfd());
ASSERT_SYS(0, 0, ftruncate(3, 1));
ASSERT_NE(MAP_FAILED,
(map = mmap(0, 1, PROT_READ | PROT_WRITE, MAP_SHARED, 3, 0)));
if (!fork()) {
// wait for other process to enter spin sem
while (*sem == 0) donothing;
// change the file mapping
map[0] = 1;
// openbsd fails with this line commented out
ASSERT_SYS(0, 0, msync(map, 1, MS_ASYNC));
// immediately wake other process
*sem = 2;
_Exit(123);
}
*sem = 1;
while (*sem == 1) donothing;
ASSERT_EQ(1, map[0]);
ASSERT_SYS(0, 1, pread(3, &byte, 1, 0));
ASSERT_EQ(1, byte);
ASSERT_EQ(1, map[0]);
ASSERT_NE(-1, wait(&ws));
ASSERT_TRUE(WIFEXITED(ws));
ASSERT_EQ(123, WEXITSTATUS(ws));
ASSERT_SYS(0, 0, munmap(map, 1));
ASSERT_SYS(0, 0, close(3));
ASSERT_SYS(0, 0, munmap(sem, 1));
}