YugabyteDB (2.13.1.0-b60, 21121d69985fbf76aa6958d8f04a9bfa936293b5)

Coverage Report

Created: 2022-03-22 16:43

/Users/deen/code/yugabyte-db/src/yb/util/shared_mem.cc
Line
Count
Source (jump to first uncovered line)
1
// Copyright (c) YugaByte, Inc.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
4
// in compliance with the License.  You may obtain a copy of the License at
5
//
6
// http://www.apache.org/licenses/LICENSE-2.0
7
//
8
// Unless required by applicable law or agreed to in writing, software distributed under the License
9
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
10
// or implied.  See the License for the specific language governing permissions and limitations
11
// under the License.
12
//
13
14
#include "yb/util/shared_mem.h"
15
16
#if defined(__linux__)
17
#include <mntent.h>
18
#include <sys/utsname.h>
19
#include <sys/syscall.h>
20
#endif
21
#include <fcntl.h>
22
#include <string>
23
#include <glog/logging.h>
24
25
#include "yb/gutil/casts.h"
26
27
#include "yb/util/errno.h"
28
#include "yb/util/format.h"
29
#include "yb/util/random_util.h"
30
#include "yb/util/scope_exit.h"
31
#include "yb/util/status_format.h"
32
33
namespace yb {
34
35
namespace {
36
37
// File name prefix for anonymous shared memory files.
38
const char* const kAnonShmFilenamePrefix = "yb_shm_anon";
39
40
// Maximum number of temporary file name collisions to tolerate before giving up.
41
// If this limit is being reached, either the files are not being removed
42
// properly, or kAnonShmFilenameRandomnessSize is too small.
43
constexpr int kMaxAnonShmFileCollisionRetries = 20;
44
45
// The length (in characters) of the random suffix generated for temporary
46
// anonymous shared memory files.
47
constexpr int kAnonShmFilenameRandomnessSize = 16;
48
49
// Helper method to memory map a shared memory file using its file descriptor.
50
23.4k
Result<void*> MMap(int fd, SharedMemorySegment::AccessMode access_mode, size_t segment_size) {
51
23.4k
  void* segment_address = mmap(
52
23.4k
      NULL /* addr */,
53
23.4k
      segment_size,
54
23.4k
      access_mode,
55
23.4k
      MAP_SHARED,
56
23.4k
      fd,
57
23.4k
      0 /* offset */);
58
23.4k
  if (segment_address == MAP_FAILED) {
59
4
    return STATUS_FORMAT(
60
4
        IOError,
61
4
        "Error mapping shared memory segment: errno=$0: $1",
62
4
        errno,
63
4
        ErrnoToString(errno));
64
4
  }
65
23.4k
  return segment_address;
66
23.4k
}
67
68
// Returns the directory in which all shared memory files should be created.
69
17.3k
std::string GetSharedMemoryDirectory() {
70
17.3k
  std::string directory = "/tmp";
71
72
#if defined(__linux__)
73
  auto* mount_file = fopen("/proc/mounts", "r");
74
  auto se = ScopeExit([&mount_file] {
75
    if (mount_file) {
76
      fclose(mount_file);
77
    }
78
  });
79
80
  if (mount_file) {
81
    while (struct mntent* mount_info = getmntent(mount_file)) {
82
      // We want a read-write tmpfs mount.
83
      if (strcmp(mount_info->mnt_type, "tmpfs") == 0
84
          && hasmntopt(mount_info, MNTOPT_RW) != nullptr) {
85
        directory = std::string(mount_info->mnt_dir);
86
87
        // Give preference to /dev/shm.
88
        if (directory == "/dev/shm") {
89
          break;
90
        }
91
      }
92
    }
93
  } else if (errno != ENOENT) {
94
    LOG(ERROR) << "Unexpected error when reading /proc/mounts: errno=" << errno
95
               << ": " << ErrnoToString(errno);
96
  }
97
#endif
98
99
17.3k
  return directory;
100
17.3k
}
101
102
#if defined(__linux__)
103
int memfd_create() {
104
  // This name doesn't really matter, it is only useful for debugging purposes.
105
  // See http://man7.org/linux/man-pages/man2/memfd_create.2.html.
106
  return narrow_cast<int>(syscall(__NR_memfd_create, kAnonShmFilenamePrefix, 0 /* flags */));
107
}
108
109
// Attempts to create a shared memory file using memfd_create.
110
// Returns the file descriptor if successful, otherwise -1 is returned.
111
int TryMemfdCreate() {
112
  int fd = -1;
113
114
  struct utsname uts_name;
115
  if (uname(&uts_name) == -1) {
116
    LOG(ERROR) << "Failed to get kernel name information: errno=" << errno
117
               << ": " << ErrnoToString(errno);
118
    return fd;
119
  }
120
121
  char* kernel_version = uts_name.release;
122
  int major_version = 0;
123
  int minor_version = 0;
124
  char garbage;
125
126
  std::stringstream version_stream;
127
  version_stream << kernel_version;
128
  version_stream >> major_version >> garbage >> minor_version;
129
130
  // Check that version is > 3.17. Note: memfd_create is available as of 3.17.8,
131
  // however we assume that any kernel with version 3.17.x does not have
132
  // memfd_create to avoid having to parse the patch version.
133
  auto combined_version = major_version * 1000 + minor_version;
134
  if (combined_version >= 3017) {
135
    LOG(INFO) << "Using memfd_create as a shared memory provider";
136
137
    fd = memfd_create();
138
    if (fd == -1) {
139
      LOG(ERROR) << "Error creating shared memory via memfd_create: errno=" << errno
140
                 << ": " << ErrnoToString(errno);
141
    }
142
  }
143
144
  return fd;
145
}
146
#endif
147
148
// Creates and unlinks a temporary file for use as a shared memory segment.
149
// Returns the file descriptor, or an error if the creation fails.
150
17.3k
Result<int> CreateTempSharedMemoryFile() {
151
17.3k
  int fd = -1;
152
153
17.3k
  std::string shared_memory_dir = GetSharedMemoryDirectory();
154
17.3k
  LOG(INFO) << "Using directory " << shared_memory_dir << " to store shared memory objects";
155
156
17.3k
  for (int attempt = 0; attempt < kMaxAnonShmFileCollisionRetries; 
++attempt0
) {
157
17.3k
    std::string temp_file_name = string(kAnonShmFilenamePrefix) + "_"
158
17.3k
        + RandomHumanReadableString(kAnonShmFilenameRandomnessSize);
159
17.3k
    std::string temp_file_path = shared_memory_dir + "/" + temp_file_name;
160
161
    // We do not use shm_open here, since shm_unlink will automatically close the file
162
    // descriptor. We want to unlink with the ability to keep the descriptor open.
163
17.3k
    fd = open(temp_file_path.c_str(), O_CREAT | O_RDWR | O_EXCL, S_IRUSR | S_IWUSR);
164
17.3k
    if (fd == -1) {
165
0
      if (errno == EEXIST) {
166
        // This file already exists, try another random name.
167
0
        continue;
168
0
      }
169
170
0
      return STATUS_FORMAT(
171
0
          IOError,
172
0
          "Error creating anonymous shared memory file: errno=$0: $1",
173
0
          errno,
174
0
          ErrnoToString(errno));
175
0
    }
176
177
    // Immediately unlink the file to so it will be removed when all file descriptors close.
178
17.3k
    if (unlink(temp_file_path.c_str()) == -1) {
179
0
      LOG(ERROR) << "Leaking shared memory file '" << temp_file_path
180
0
                 << "' after failure to unlink: errno=" << errno
181
0
                 << ": " << ErrnoToString(errno);
182
0
    }
183
17.3k
    break;
184
17.3k
  }
185
186
17.3k
  if (fd == -1) {
187
0
    return STATUS_FORMAT(
188
0
        InternalError,
189
0
        "Giving up creating anonymous shared memory segment after $0 failed attempts",
190
0
        kMaxAnonShmFileCollisionRetries);
191
0
  }
192
193
17.3k
  return fd;
194
17.3k
}
195
196
}  // namespace
197
198
17.3k
Result<SharedMemorySegment> SharedMemorySegment::Create(size_t segment_size) {
199
17.3k
  int fd = -1;
200
17.3k
  bool auto_close_fd = true;
201
17.3k
  auto se = ScopeExit([&fd, &auto_close_fd] {
202
17.3k
    if (fd != -1 && auto_close_fd) {
203
1
      close(fd);
204
1
    }
205
17.3k
  });
206
207
#if defined(__linux__)
208
  // Prefer memfd_create over creating temporary files, if available.
209
  fd = TryMemfdCreate();
210
#endif
211
212
17.3k
  if (fd == -1) {
213
17.3k
    fd = VERIFY_RESULT(CreateTempSharedMemoryFile());
214
17.3k
  }
215
216
  // If we have made it here, we should have a valid shared memory file.
217
17.3k
  DCHECK_NE(fd, -1);
218
219
17.3k
  if (ftruncate(fd, segment_size) == -1) {
220
1
    return STATUS_FORMAT(
221
1
        IOError,
222
1
        "Error truncating shared memory segment: errno=$0: $1",
223
1
        errno,
224
1
        ErrnoToString(errno));
225
1
  }
226
227
17.3k
  void* segment_address = VERIFY_RESULT(MMap(fd, AccessMode::kReadWrite, segment_size));
228
229
0
  auto_close_fd = false;
230
17.3k
  return SharedMemorySegment(segment_address, fd, segment_size);
231
17.3k
}
232
233
Result<SharedMemorySegment> SharedMemorySegment::Open(
234
    int fd,
235
    AccessMode access_mode,
236
6.11k
    size_t segment_size) {
237
6.11k
  void* segment_address = 
VERIFY_RESULT6.10k
(MMap(fd, access_mode, segment_size));6.10k
238
0
  return SharedMemorySegment(DCHECK_NOTNULL(segment_address), fd, segment_size);
239
6.11k
}
240
241
SharedMemorySegment::SharedMemorySegment(void* base_address, int fd, size_t segment_size)
242
    : base_address_(base_address),
243
      fd_(fd),
244
23.4k
      segment_size_(segment_size) {
245
23.4k
}
246
247
SharedMemorySegment::SharedMemorySegment(SharedMemorySegment&& other)
248
    : base_address_(other.base_address_),
249
      fd_(other.fd_),
250
123k
      segment_size_(other.segment_size_) {
251
123k
  other.base_address_ = nullptr;
252
123k
  other.fd_ = -1;
253
123k
}
254
255
129k
SharedMemorySegment::~SharedMemorySegment() {
256
129k
  if (base_address_ && 
munmap(base_address_, segment_size_) == -16.36k
) {
257
0
    LOG(ERROR) << "Failed to unmap shared memory segment: errno=" << errno
258
0
               << ": " << ErrnoToString(errno);
259
0
  }
260
261
129k
  if (fd_ != -1) {
262
6.36k
    close(fd_);
263
6.36k
  }
264
129k
}
265
266
515k
void* SharedMemorySegment::GetAddress() const {
267
515k
  return base_address_;
268
515k
}
269
270
2.04k
int SharedMemorySegment::GetFd() const {
271
2.04k
  return fd_;
272
2.04k
}
273
274
}  // namespace yb