1/*
2 * Copyright 2013-present Facebook, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#pragma once
18
19#include <boost/noncopyable.hpp>
20#include <glog/logging.h>
21
22#include <folly/File.h>
23#include <folly/Range.h>
24
25namespace folly {
26
27/**
28 * Maps files in memory (read-only).
29 *
30 * @author Tudor Bosman (tudorb@fb.com)
31 */
32class MemoryMapping : boost::noncopyable {
33 public:
34 /**
35 * Lock the pages in memory?
36 * TRY_LOCK = try to lock, log warning if permission denied
37 * MUST_LOCK = lock, fail assertion if permission denied.
38 */
39 enum class LockMode {
40 TRY_LOCK,
41 MUST_LOCK,
42 };
43 /**
44 * Map a portion of the file indicated by filename in memory, causing a CHECK
45 * failure on error.
46 *
47 * By default, map the whole file. length=-1: map from offset to EOF.
48 * Unlike the mmap() system call, offset and length don't need to be
49 * page-aligned. length is clipped to the end of the file if it's too large.
50 *
51 * The mapping will be destroyed (and the memory pointed-to by data() will
52 * likely become inaccessible) when the MemoryMapping object is destroyed.
53 */
54 struct Options {
55 Options() {}
56
57 // Convenience methods; return *this for chaining.
58 Options& setPageSize(off_t v) {
59 pageSize = v;
60 return *this;
61 }
62 Options& setShared(bool v) {
63 shared = v;
64 return *this;
65 }
66 Options& setPrefault(bool v) {
67 prefault = v;
68 return *this;
69 }
70 Options& setReadable(bool v) {
71 readable = v;
72 return *this;
73 }
74 Options& setWritable(bool v) {
75 writable = v;
76 return *this;
77 }
78 Options& setGrow(bool v) {
79 grow = v;
80 return *this;
81 }
82
83 // Page size. 0 = use appropriate page size.
84 // (On Linux, we use a huge page size if the file is on a hugetlbfs
85 // file system, and the default page size otherwise)
86 off_t pageSize = 0;
87
88 // If shared (default), the memory mapping is shared with other processes
89 // mapping the same file (or children); if not shared (private), each
90 // process has its own mapping. Changes in writable, private mappings are
91 // not reflected to the underlying file. See the discussion of
92 // MAP_PRIVATE vs MAP_SHARED in the mmap(2) manual page.
93 bool shared = true;
94
95 // Populate page tables; subsequent accesses should not be blocked
96 // by page faults. This is a hint, as it may not be supported.
97 bool prefault = false;
98
99 // Map the pages readable. Note that mapping pages without read permissions
100 // is not universally supported (not supported on hugetlbfs on Linux, for
101 // example)
102 bool readable = true;
103
104 // Map the pages writable.
105 bool writable = false;
106
107 // When mapping a file in writable mode, grow the file to the requested
108 // length (using ftruncate()) before mapping; if false, truncate the
109 // mapping to the actual file size instead.
110 bool grow = false;
111
112 // Fix map at this address, if not nullptr. Must be aligned to a multiple
113 // of the appropriate page size.
114 void* address = nullptr;
115 };
116
117 // Options to emulate the old WritableMemoryMapping: readable and writable,
118 // allow growing the file if mapping past EOF.
119 static Options writable() {
120 return Options().setWritable(true).setGrow(true);
121 }
122
123 enum AnonymousType {
124 kAnonymous,
125 };
126
127 /**
128 * Create an anonymous mapping.
129 */
130 MemoryMapping(AnonymousType, off_t length, Options options = Options());
131
132 explicit MemoryMapping(
133 File file,
134 off_t offset = 0,
135 off_t length = -1,
136 Options options = Options());
137
138 explicit MemoryMapping(
139 const char* name,
140 off_t offset = 0,
141 off_t length = -1,
142 Options options = Options());
143
144 explicit MemoryMapping(
145 int fd,
146 off_t offset = 0,
147 off_t length = -1,
148 Options options = Options());
149
150 MemoryMapping(MemoryMapping&&) noexcept;
151
152 ~MemoryMapping();
153
154 MemoryMapping& operator=(MemoryMapping);
155
156 void swap(MemoryMapping& other) noexcept;
157
158 /**
159 * Lock the pages in memory
160 */
161 bool mlock(LockMode lock);
162
163 /**
164 * Unlock the pages.
165 * If dontneed is true, the kernel is instructed to release these pages
166 * (per madvise(MADV_DONTNEED)).
167 */
168 void munlock(bool dontneed = false);
169
170 /**
171 * Hint that these pages will be scanned linearly.
172 * madvise(MADV_SEQUENTIAL)
173 */
174 void hintLinearScan();
175
176 /**
177 * Advise the kernel about memory access.
178 */
179 void advise(int advice) const;
180 void advise(int advice, size_t offset, size_t length) const;
181
182 /**
183 * A bitwise cast of the mapped bytes as range of values. Only intended for
184 * use with POD or in-place usable types.
185 */
186 template <class T>
187 Range<const T*> asRange() const {
188 size_t count = data_.size() / sizeof(T);
189 return Range<const T*>(
190 static_cast<const T*>(static_cast<const void*>(data_.data())), count);
191 }
192
193 /**
194 * A range of bytes mapped by this mapping.
195 */
196 ByteRange range() const {
197 return data_;
198 }
199
200 /**
201 * A bitwise cast of the mapped bytes as range of mutable values. Only
202 * intended for use with POD or in-place usable types.
203 */
204 template <class T>
205 Range<T*> asWritableRange() const {
206 DCHECK(options_.writable); // you'll segfault anyway...
207 size_t count = data_.size() / sizeof(T);
208 return Range<T*>(static_cast<T*>(static_cast<void*>(data_.data())), count);
209 }
210
211 /**
212 * A range of mutable bytes mapped by this mapping.
213 */
214 MutableByteRange writableRange() const {
215 DCHECK(options_.writable); // you'll segfault anyway...
216 return data_;
217 }
218
219 /**
220 * Return the memory area where the file was mapped.
221 * Deprecated; use range() instead.
222 */
223 StringPiece data() const {
224 return asRange<const char>();
225 }
226
227 bool mlocked() const {
228 return locked_;
229 }
230
231 int fd() const {
232 return file_.fd();
233 }
234
235 private:
236 MemoryMapping();
237
238 enum InitFlags {
239 kGrow = 1 << 0,
240 kAnon = 1 << 1,
241 };
242 void init(off_t offset, off_t length);
243
244 File file_;
245 void* mapStart_ = nullptr;
246 off_t mapLength_ = 0;
247 Options options_;
248 bool locked_ = false;
249 MutableByteRange data_;
250};
251
252void swap(MemoryMapping&, MemoryMapping&) noexcept;
253
254/**
255 * A special case of memcpy() that always copies memory forwards.
256 * (libc's memcpy() is allowed to copy memory backwards, and will do so
257 * when using SSSE3 instructions).
258 *
259 * Assumes src and dest are aligned to alignof(unsigned long).
260 *
261 * Useful when copying from/to memory mappings after hintLinearScan();
262 * copying backwards renders any prefetching useless (even harmful).
263 */
264void alignedForwardMemcpy(void* dest, const void* src, size_t size);
265
266/**
267 * Copy a file using mmap(). Overwrites dest.
268 */
269void mmapFileCopy(const char* src, const char* dest, mode_t mode = 0666);
270
271} // namespace folly
272