CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
hrydgard

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: hrydgard/ppsspp
Path: blob/master/Common/MemArenaPosix.cpp
Views: 1401
1
// Copyright (C) 2003 Dolphin Project.
2
3
// This program is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, version 2.0 or later versions.
6
7
// This program is distributed in the hope that it will be useful,
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
// GNU General Public License 2.0 for more details.
11
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
14
15
// Official SVN repository and contact information can be found at
16
// http://code.google.com/p/dolphin-emu/
17
18
#include "ppsspp_config.h"
19
20
#if !defined(_WIN32) && !defined(ANDROID) && !defined(__APPLE__) && !PPSSPP_PLATFORM(SWITCH)
21
22
#include <sys/mman.h>
23
#include <sys/stat.h>
24
#include <fcntl.h>
25
#include <unistd.h>
26
#include <cerrno>
27
#include <cstring>
28
#include <string>
29
30
#ifndef MAP_NORESERVE
31
// Not implemented on BSDs
32
#define MAP_NORESERVE 0
33
#endif
34
35
#include "Common/Log.h"
36
#include "Common/File/FileUtil.h"
37
#include "Common/MemoryUtil.h"
38
#include "Common/MemArena.h"
39
40
static const std::string tmpfs_location = "/dev/shm";
41
static const std::string tmpfs_ram_temp_file = "/dev/shm/gc_mem.tmp";
42
43
// do not make this "static"
44
std::string ram_temp_file = "/tmp/gc_mem.tmp";
45
46
size_t MemArena::roundup(size_t x) {
47
return x;
48
}
49
50
bool MemArena::NeedsProbing() {
51
return false;
52
}
53
54
bool MemArena::GrabMemSpace(size_t size) {
55
#ifndef NO_MMAP
56
constexpr mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
57
58
// Try a few times in case multiple instances are started near each other.
59
char ram_temp_filename[128]{};
60
bool is_shm = false;
61
for (int i = 0; i < 256; ++i) {
62
snprintf(ram_temp_filename, sizeof(ram_temp_filename), "/ppsspp_%d.ram", i);
63
// This opens atomically, so will fail if another process is starting.
64
fd = shm_open(ram_temp_filename, O_RDWR | O_CREAT | O_EXCL, mode);
65
if (fd >= 0) {
66
INFO_LOG(Log::MemMap, "Got shm file: %s", ram_temp_filename);
67
is_shm = true;
68
// Our handle persists per POSIX, so no need to keep it around.
69
if (shm_unlink(ram_temp_filename) != 0) {
70
WARN_LOG(Log::MemMap, "Failed to shm_unlink %s", ram_temp_file.c_str());
71
}
72
break;
73
}
74
}
75
76
// Fall back to old tmpfs behavior.
77
if (fd < 0 && File::Exists(Path(tmpfs_location))) {
78
fd = open(tmpfs_ram_temp_file.c_str(), O_RDWR | O_CREAT, mode);
79
if (fd >= 0) {
80
// Great, this definitely shouldn't flush to disk.
81
ram_temp_file = tmpfs_ram_temp_file;
82
INFO_LOG(Log::MemMap, "Got tmpfs ram file: %s", tmpfs_ram_temp_file.c_str());
83
}
84
}
85
86
if (fd < 0) {
87
INFO_LOG(Log::MemMap, "Trying '%s' as ram temp file", ram_temp_file.c_str());
88
fd = open(ram_temp_file.c_str(), O_RDWR | O_CREAT, mode);
89
}
90
if (fd < 0) {
91
ERROR_LOG(Log::MemMap, "Failed to grab memory space as a file: %s of size: %08x. Error: %s", ram_temp_file.c_str(), (int)size, strerror(errno));
92
return false;
93
}
94
// delete immediately, we keep the fd so it still lives
95
if (!is_shm && unlink(ram_temp_file.c_str()) != 0) {
96
WARN_LOG(Log::MemMap, "Failed to unlink %s", ram_temp_file.c_str());
97
}
98
if (ftruncate(fd, size) != 0) {
99
ERROR_LOG(Log::MemMap, "Failed to ftruncate %d (%s) to size %08x", (int)fd, ram_temp_file.c_str(), (int)size);
100
// Should this be a failure?
101
}
102
#endif
103
return true;
104
}
105
106
void MemArena::ReleaseSpace() {
107
#ifndef NO_MMAP
108
close(fd);
109
#endif
110
}
111
112
void *MemArena::CreateView(s64 offset, size_t size, void *base)
113
{
114
#ifdef NO_MMAP
115
return (void*) base;
116
#else
117
void *retval = mmap(base, size, PROT_READ | PROT_WRITE, MAP_SHARED |
118
// Do not sync memory to underlying file. Linux has this by default.
119
#if defined(__DragonFly__) || defined(__FreeBSD__)
120
MAP_NOSYNC |
121
#endif
122
((base == 0) ? 0 : MAP_FIXED), fd, offset);
123
124
if (retval == MAP_FAILED) {
125
NOTICE_LOG(Log::MemMap, "mmap on %s (fd: %d) failed: %s", ram_temp_file.c_str(), (int)fd, strerror(errno));
126
return 0;
127
}
128
return retval;
129
#endif
130
}
131
132
void MemArena::ReleaseView(s64 offset, void* view, size_t size) {
133
#ifndef NO_MMAP
134
munmap(view, size);
135
#endif
136
}
137
138
u8* MemArena::Find4GBBase() {
139
// Now, create views in high memory where there's plenty of space.
140
#if PPSSPP_ARCH(64BIT) && !defined(USE_ASAN) && !defined(NO_MMAP)
141
// We should probably just go look in /proc/self/maps for some free space.
142
// But let's try the anonymous mmap trick, just like on 32-bit, but bigger and
143
// aligned to 4GB for the movk trick. We can ensure that we get an aligned 4GB
144
// address by grabbing 8GB and aligning the pointer.
145
const uint64_t EIGHT_GIGS = 0x200000000ULL;
146
void *base = mmap(0, EIGHT_GIGS, PROT_NONE, MAP_ANON | MAP_PRIVATE | MAP_NORESERVE, -1, 0);
147
if (base && base != MAP_FAILED) {
148
INFO_LOG(Log::MemMap, "base: %p", base);
149
uint64_t aligned_base = ((uint64_t)base + 0xFFFFFFFF) & ~0xFFFFFFFFULL;
150
INFO_LOG(Log::MemMap, "aligned_base: %p", (void *)aligned_base);
151
munmap(base, EIGHT_GIGS);
152
return reinterpret_cast<u8 *>(aligned_base);
153
} else {
154
u8 *hardcoded_ptr = reinterpret_cast<u8*>(0x2300000000ULL);
155
INFO_LOG(Log::MemMap, "Failed to anonymously map 8GB (%s). Fall back to the hardcoded pointer %p.", strerror(errno), hardcoded_ptr);
156
// Just grab some random 4GB...
157
// This has been known to fail lately though, see issue #12249.
158
return hardcoded_ptr;
159
}
160
#elif defined(NO_MMAP)
161
void* base = std::malloc(0x0A000000);
162
return static_cast<u8*>(base);
163
#else
164
size_t size = 0x10000000;
165
void* base = mmap(0, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED | MAP_NORESERVE, -1, 0);
166
_assert_msg_(base != MAP_FAILED, "Failed to map 256 MB of memory space: %s", strerror(errno));
167
munmap(base, size);
168
return static_cast<u8*>(base);
169
#endif
170
}
171
172
#endif
173
174