1 // mmap_v1_extent_manager.cpp
2
3
4 /**
5 * Copyright (C) 2018-present MongoDB, Inc.
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the Server Side Public License, version 1,
9 * as published by MongoDB, Inc.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * Server Side Public License for more details.
15 *
16 * You should have received a copy of the Server Side Public License
17 * along with this program. If not, see
18 * <http://www.mongodb.com/licensing/server-side-public-license>.
19 *
20 * As a special exception, the copyright holders give permission to link the
21 * code of portions of this program with the OpenSSL library under certain
22 * conditions as described in each individual source file and distribute
23 * linked combinations including the program with the OpenSSL library. You
24 * must comply with the Server Side Public License in all respects for
25 * all of the code used other than as permitted herein. If you modify file(s)
26 * with this exception, you may extend this exception to your version of the
27 * file(s), but you are not obligated to do so. If you do not wish to do so,
28 * delete this exception statement from your version. If you delete this
29 * exception statement from all source files in the program, then also delete
30 * it in the license file.
31 */
32
33 #define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kStorage
34
35 #include <boost/filesystem/operations.hpp>
36
37 #include "mongo/db/storage/mmap_v1/mmap_v1_extent_manager.h"
38
39 #include "mongo/base/counter.h"
40 #include "mongo/db/audit.h"
41 #include "mongo/db/client.h"
42 #include "mongo/db/concurrency/d_concurrency.h"
43 #include "mongo/db/operation_context.h"
44 #include "mongo/db/service_context.h"
45 #include "mongo/db/storage/mmap_v1/data_file.h"
46 #include "mongo/db/storage/mmap_v1/dur.h"
47 #include "mongo/db/storage/mmap_v1/extent.h"
48 #include "mongo/db/storage/mmap_v1/extent_manager.h"
49 #include "mongo/db/storage/mmap_v1/mmap.h"
50 #include "mongo/db/storage/mmap_v1/mmap_v1_engine.h"
51 #include "mongo/db/storage/mmap_v1/mmap_v1_options.h"
52 #include "mongo/db/storage/mmap_v1/record.h"
53 #include "mongo/db/storage/record_fetcher.h"
54 #include "mongo/stdx/memory.h"
55 #include "mongo/util/fail_point_service.h"
56 #include "mongo/util/file.h"
57 #include "mongo/util/log.h"
58
59 namespace mongo {
60
61 using std::unique_ptr;
62 using std::endl;
63 using std::max;
64 using std::string;
65 using std::stringstream;
66
67 // Turn on this failpoint to force the system to yield for a fetch. Setting to "alwaysOn"
68 // will cause yields for fetching to occur on every 'kNeedsFetchFailFreq'th call to
69 // recordNeedsFetch().
70 static const int kNeedsFetchFailFreq = 2;
71 static Counter64 needsFetchFailCounter;
72 MONGO_FP_DECLARE(recordNeedsFetchFail);
73
74 // Used to make sure the compiler doesn't get too smart on us when we're
75 // trying to touch records.
76 // volatile - avoid compiler optimizations for touching a mmap page
77 volatile int __record_touch_dummy = 1; // NOLINT
78
79 class MmapV1RecordFetcher : public RecordFetcher {
80 MONGO_DISALLOW_COPYING(MmapV1RecordFetcher);
81
82 public:
MmapV1RecordFetcher(const MmapV1RecordHeader * record)83 explicit MmapV1RecordFetcher(const MmapV1RecordHeader* record) : _record(record) {}
84
setup(OperationContext * opCtx)85 virtual void setup(OperationContext* opCtx) {
86 invariant(!_filesLock.get());
87 _filesLock.reset(new LockMongoFilesShared(opCtx));
88 }
89
fetch()90 virtual void fetch() {
91 // It's only legal to touch the record while we're holding a lock on the data files.
92 invariant(_filesLock.get());
93
94 const char* recordChar = reinterpret_cast<const char*>(_record);
95
96 // Here's where we actually deference a pointer into the record. This is where
97 // we expect a page fault to occur, so we should this out of the lock.
98 __record_touch_dummy += *recordChar;
99
100 // We're not going to touch the record anymore, so we can give up our
101 // lock on mongo files. We do this here because we have to release the
102 // lock on mongo files prior to reacquiring lock mgr locks.
103 _filesLock.reset();
104 }
105
106 private:
107 // The record which needs to be touched in order to page fault. Not owned by us.
108 const MmapV1RecordHeader* _record;
109
110 // This ensures that our MmapV1RecordHeader* does not drop out from under our feet before
111 // we dereference it.
112 std::unique_ptr<LockMongoFilesShared> _filesLock;
113 };
114
MmapV1ExtentManager(StringData dbname,StringData path,bool directoryPerDB)115 MmapV1ExtentManager::MmapV1ExtentManager(StringData dbname, StringData path, bool directoryPerDB)
116 : _dbname(dbname.toString()),
117 _path(path.toString()),
118 _directoryPerDB(directoryPerDB),
119 _rid(RESOURCE_METADATA, dbname) {
120 StorageEngine* engine = getGlobalServiceContext()->getGlobalStorageEngine();
121 invariant(engine->isMmapV1());
122 MMAPV1Engine* mmapEngine = static_cast<MMAPV1Engine*>(engine);
123 _recordAccessTracker = &mmapEngine->getRecordAccessTracker();
124 }
125
create(StringData dbname,StringData path,bool directoryPerDB)126 std::unique_ptr<ExtentManager> MmapV1ExtentManager::Factory::create(StringData dbname,
127 StringData path,
128 bool directoryPerDB) {
129 return stdx::make_unique<MmapV1ExtentManager>(
130 std::move(dbname), std::move(path), directoryPerDB);
131 }
132
_fileName(int n) const133 boost::filesystem::path MmapV1ExtentManager::_fileName(int n) const {
134 stringstream ss;
135 ss << _dbname << '.' << n;
136 boost::filesystem::path fullName(_path);
137 if (_directoryPerDB)
138 fullName /= _dbname;
139 fullName /= ss.str();
140 return fullName;
141 }
142
143
init(OperationContext * opCtx)144 Status MmapV1ExtentManager::init(OperationContext* opCtx) {
145 invariant(_files.empty());
146
147 for (int n = 0; n < DiskLoc::MaxFiles; n++) {
148 const boost::filesystem::path fullName = _fileName(n);
149 if (!boost::filesystem::exists(fullName)) {
150 break;
151 }
152
153 const std::string fullNameString = fullName.string();
154
155 {
156 // If the file is uninitialized we exit the loop because it is just prealloced. We
157 // do this on a bare File object rather than using the DataFile because closing a
158 // DataFile triggers dur::closingFileNotification() which is fatal if there are any
159 // pending writes. Therefore we must only open files that we know we want to keep.
160 File preview;
161 preview.open(fullNameString.c_str(), /*readOnly*/ true);
162 invariant(preview.is_open());
163
164 // File can't be initialized if too small.
165 if (preview.len() < sizeof(DataFileHeader)) {
166 break;
167 }
168
169 // This is the equivalent of DataFileHeader::uninitialized().
170 int version;
171 preview.read(0, reinterpret_cast<char*>(&version), sizeof(version));
172 invariant(!preview.bad());
173 if (version == 0) {
174 break;
175 }
176 }
177
178 unique_ptr<DataFile> df(new DataFile(opCtx, n));
179
180 Status s = df->openExisting(opCtx, fullNameString.c_str());
181 if (!s.isOK()) {
182 df->close(opCtx);
183 return s;
184 }
185
186 invariant(!df->getHeader()->uninitialized());
187
188 // We only checkUpgrade on files that we are keeping, not preallocs.
189 df->getHeader()->checkUpgrade(opCtx);
190
191 _files.push_back(df.release());
192 }
193
194 // If this is a new database being created, instantiate the first file and one extent so
195 // we can have a coherent database.
196 if (_files.empty()) {
197 WriteUnitOfWork wuow(opCtx);
198 _createExtent(opCtx, initialSize(128), false);
199 wuow.commit();
200
201 // Commit the journal and all changes to disk so that even if exceptions occur during
202 // subsequent initialization, we won't have uncommited changes during file close.
203 getDur().commitNow(opCtx);
204 }
205
206 return Status::OK();
207 }
208
_getOpenFile(int fileId) const209 const DataFile* MmapV1ExtentManager::_getOpenFile(int fileId) const {
210 if (fileId < 0 || fileId >= _files.size()) {
211 log() << "_getOpenFile() invalid file index requested " << fileId;
212 invariant(false);
213 }
214
215 return _files[fileId];
216 }
217
_getOpenFile(int fileId)218 DataFile* MmapV1ExtentManager::_getOpenFile(int fileId) {
219 if (fileId < 0 || fileId >= _files.size()) {
220 log() << "_getOpenFile() invalid file index requested " << fileId;
221 invariant(false);
222 }
223
224 return _files[fileId];
225 }
226
_addAFile(OperationContext * opCtx,int sizeNeeded,bool preallocateNextFile)227 DataFile* MmapV1ExtentManager::_addAFile(OperationContext* opCtx,
228 int sizeNeeded,
229 bool preallocateNextFile) {
230 // Database must be stable and we need to be in some sort of an update operation in order
231 // to add a new file.
232 invariant(opCtx->lockState()->isDbLockedForMode(_dbname, MODE_IX));
233
234 const int allocFileId = _files.size();
235
236 int minSize = 0;
237 if (allocFileId > 0) {
238 // Make the next file at least as large as the previous
239 minSize = _files[allocFileId - 1]->getHeader()->fileLength;
240 }
241
242 if (minSize < sizeNeeded + DataFileHeader::HeaderSize) {
243 minSize = sizeNeeded + DataFileHeader::HeaderSize;
244 }
245
246 {
247 unique_ptr<DataFile> allocFile(new DataFile(opCtx, allocFileId));
248 const string allocFileName = _fileName(allocFileId).string();
249
250 Timer t;
251
252 try {
253 allocFile->open(opCtx, allocFileName.c_str(), minSize, false);
254 } catch (...) {
255 allocFile->close(opCtx);
256 throw;
257 }
258 if (t.seconds() > 1) {
259 log() << "MmapV1ExtentManager took " << t.seconds()
260 << " seconds to open: " << allocFileName;
261 }
262
263 // It's all good
264 _files.push_back(allocFile.release());
265 }
266
267 // Preallocate is asynchronous
268 if (preallocateNextFile) {
269 unique_ptr<DataFile> nextFile(new DataFile(opCtx, allocFileId + 1));
270 const string nextFileName = _fileName(allocFileId + 1).string();
271
272 try {
273 nextFile->open(opCtx, nextFileName.c_str(), minSize, false);
274 } catch (...) {
275 nextFile->close(opCtx);
276 throw;
277 }
278 }
279
280 // Returns the last file added
281 return _files[allocFileId];
282 }
283
numFiles() const284 int MmapV1ExtentManager::numFiles() const {
285 return _files.size();
286 }
287
fileSize() const288 long long MmapV1ExtentManager::fileSize() const {
289 long long size = 0;
290 for (int n = 0; boost::filesystem::exists(_fileName(n)); n++) {
291 size += boost::filesystem::file_size(_fileName(n));
292 }
293
294 return size;
295 }
296
_recordForV1(const DiskLoc & loc) const297 MmapV1RecordHeader* MmapV1ExtentManager::_recordForV1(const DiskLoc& loc) const {
298 loc.assertOk();
299 const DataFile* df = _getOpenFile(loc.a());
300
301 int ofs = loc.getOfs();
302 if (ofs < DataFileHeader::HeaderSize) {
303 df->badOfs(ofs); // will msgassert - external call to keep out of the normal code path
304 }
305
306 return reinterpret_cast<MmapV1RecordHeader*>(df->p() + ofs);
307 }
308
recordForV1(const DiskLoc & loc) const309 MmapV1RecordHeader* MmapV1ExtentManager::recordForV1(const DiskLoc& loc) const {
310 MmapV1RecordHeader* record = _recordForV1(loc);
311 _recordAccessTracker->markAccessed(record);
312 return record;
313 }
314
recordNeedsFetch(const DiskLoc & loc) const315 std::unique_ptr<RecordFetcher> MmapV1ExtentManager::recordNeedsFetch(const DiskLoc& loc) const {
316 if (loc.isNull())
317 return {};
318 MmapV1RecordHeader* record = _recordForV1(loc);
319
320 // For testing: if failpoint is enabled we randomly request fetches without
321 // going to the RecordAccessTracker.
322 if (MONGO_FAIL_POINT(recordNeedsFetchFail)) {
323 needsFetchFailCounter.increment();
324 if ((needsFetchFailCounter.get() % kNeedsFetchFailFreq) == 0) {
325 return stdx::make_unique<MmapV1RecordFetcher>(record);
326 }
327 }
328
329 if (!_recordAccessTracker->checkAccessedAndMark(record)) {
330 return stdx::make_unique<MmapV1RecordFetcher>(record);
331 }
332
333 return {};
334 }
335
extentLocForV1(const DiskLoc & loc) const336 DiskLoc MmapV1ExtentManager::extentLocForV1(const DiskLoc& loc) const {
337 MmapV1RecordHeader* record = recordForV1(loc);
338 return DiskLoc(loc.a(), record->extentOfs());
339 }
340
extentForV1(const DiskLoc & loc) const341 Extent* MmapV1ExtentManager::extentForV1(const DiskLoc& loc) const {
342 DiskLoc extentLoc = extentLocForV1(loc);
343 return getExtent(extentLoc);
344 }
345
getExtent(const DiskLoc & loc,bool doSanityCheck) const346 Extent* MmapV1ExtentManager::getExtent(const DiskLoc& loc, bool doSanityCheck) const {
347 loc.assertOk();
348 Extent* e = reinterpret_cast<Extent*>(_getOpenFile(loc.a())->p() + loc.getOfs());
349 if (doSanityCheck)
350 e->assertOk();
351
352 _recordAccessTracker->markAccessed(e);
353
354 return e;
355 }
356
_checkQuota(bool enforceQuota,int fileNo)357 void _checkQuota(bool enforceQuota, int fileNo) {
358 if (!enforceQuota)
359 return;
360
361 if (fileNo < mmapv1GlobalOptions.quotaFiles)
362 return;
363
364 uasserted(12501, "quota exceeded");
365 }
366
maxSize() const367 int MmapV1ExtentManager::maxSize() const {
368 return DataFile::maxSize() - DataFileHeader::HeaderSize - 16;
369 }
370
_createExtentInFile(OperationContext * opCtx,int fileNo,DataFile * f,int size,bool enforceQuota)371 DiskLoc MmapV1ExtentManager::_createExtentInFile(
372 OperationContext* opCtx, int fileNo, DataFile* f, int size, bool enforceQuota) {
373 _checkQuota(enforceQuota, fileNo - 1);
374
375 massert(10358, "bad new extent size", size >= minSize() && size <= maxSize());
376
377 DiskLoc loc = f->allocExtentArea(opCtx, size);
378 loc.assertOk();
379
380 Extent* e = getExtent(loc, false);
381 verify(e);
382
383 *opCtx->recoveryUnit()->writing(&e->magic) = Extent::extentSignature;
384 *opCtx->recoveryUnit()->writing(&e->myLoc) = loc;
385 *opCtx->recoveryUnit()->writing(&e->length) = size;
386
387 return loc;
388 }
389
390
_createExtent(OperationContext * opCtx,int size,bool enforceQuota)391 DiskLoc MmapV1ExtentManager::_createExtent(OperationContext* opCtx, int size, bool enforceQuota) {
392 size = quantizeExtentSize(size);
393
394 if (size > maxSize())
395 size = maxSize();
396
397 verify(size < DataFile::maxSize());
398
399 for (int i = numFiles() - 1; i >= 0; i--) {
400 DataFile* f = _getOpenFile(i);
401 invariant(f);
402
403 if (f->getHeader()->unusedLength >= size) {
404 return _createExtentInFile(opCtx, i, f, size, enforceQuota);
405 }
406 }
407
408 _checkQuota(enforceQuota, numFiles());
409
410 // no space in an existing file
411 // allocate files until we either get one big enough or hit maxSize
412 for (int i = 0; i < 8; i++) {
413 DataFile* f = _addAFile(opCtx, size, false);
414
415 if (f->getHeader()->unusedLength >= size) {
416 return _createExtentInFile(opCtx, numFiles() - 1, f, size, enforceQuota);
417 }
418 }
419
420 // callers don't check for null return code, so assert
421 msgasserted(14810, "couldn't allocate space for a new extent");
422 }
423
_allocFromFreeList(OperationContext * opCtx,int approxSize,bool capped)424 DiskLoc MmapV1ExtentManager::_allocFromFreeList(OperationContext* opCtx,
425 int approxSize,
426 bool capped) {
427 // setup extent constraints
428
429 int low, high;
430 if (capped) {
431 // be strict about the size
432 low = approxSize;
433 if (low > 2048)
434 low -= 256;
435 high = (int)(approxSize * 1.05) + 256;
436 } else {
437 low = (int)(approxSize * 0.8);
438 high = (int)(approxSize * 1.4);
439 }
440 if (high <= 0) {
441 // overflowed
442 high = max(approxSize, maxSize());
443 }
444 if (high <= minSize()) {
445 // the minimum extent size is 4097
446 high = minSize() + 1;
447 }
448
449 // scan free list looking for something suitable
450
451 int n = 0;
452 Extent* best = 0;
453 int bestDiff = 0x7fffffff;
454 {
455 Timer t;
456 DiskLoc L = _getFreeListStart();
457 while (!L.isNull()) {
458 Extent* e = getExtent(L);
459 if (e->length >= low && e->length <= high) {
460 int diff = abs(e->length - approxSize);
461 if (diff < bestDiff) {
462 bestDiff = diff;
463 best = e;
464 if (((double)diff) / approxSize < 0.1) {
465 // close enough
466 break;
467 }
468 if (t.seconds() >= 2) {
469 // have spent lots of time in write lock, and we are in [low,high], so close
470 // enough could come into play if extent freelist is very long
471 break;
472 }
473 } else {
474 OCCASIONALLY {
475 if (high < 64 * 1024 && t.seconds() >= 2) {
476 // be less picky if it is taking a long time
477 high = 64 * 1024;
478 }
479 }
480 }
481 }
482 L = e->xnext;
483 ++n;
484 }
485 if (t.seconds() >= 10) {
486 log() << "warning: slow scan in allocFromFreeList (in write lock)" << endl;
487 }
488 }
489
490 if (n > 128) {
491 LOG(n < 512 ? 1 : 0) << "warning: newExtent " << n << " scanned\n";
492 }
493
494 if (!best)
495 return DiskLoc();
496
497 // remove from the free list
498 if (!best->xprev.isNull())
499 *opCtx->recoveryUnit()->writing(&getExtent(best->xprev)->xnext) = best->xnext;
500 if (!best->xnext.isNull())
501 *opCtx->recoveryUnit()->writing(&getExtent(best->xnext)->xprev) = best->xprev;
502 if (_getFreeListStart() == best->myLoc)
503 _setFreeListStart(opCtx, best->xnext);
504 if (_getFreeListEnd() == best->myLoc)
505 _setFreeListEnd(opCtx, best->xprev);
506
507 return best->myLoc;
508 }
509
allocateExtent(OperationContext * opCtx,bool capped,int size,bool enforceQuota)510 DiskLoc MmapV1ExtentManager::allocateExtent(OperationContext* opCtx,
511 bool capped,
512 int size,
513 bool enforceQuota) {
514 Lock::ResourceLock rlk(opCtx->lockState(), _rid, MODE_X);
515 bool fromFreeList = true;
516 DiskLoc eloc = _allocFromFreeList(opCtx, size, capped);
517 if (eloc.isNull()) {
518 fromFreeList = false;
519 eloc = _createExtent(opCtx, size, enforceQuota);
520 }
521
522 invariant(!eloc.isNull());
523 invariant(eloc.isValid());
524
525 LOG(1) << "MmapV1ExtentManager::allocateExtent"
526 << " desiredSize:" << size << " fromFreeList: " << fromFreeList << " eloc: " << eloc;
527
528 return eloc;
529 }
530
freeExtent(OperationContext * opCtx,DiskLoc firstExt)531 void MmapV1ExtentManager::freeExtent(OperationContext* opCtx, DiskLoc firstExt) {
532 Lock::ResourceLock rlk(opCtx->lockState(), _rid, MODE_X);
533 Extent* e = getExtent(firstExt);
534 opCtx->recoveryUnit()->writing(&e->xnext)->Null();
535 opCtx->recoveryUnit()->writing(&e->xprev)->Null();
536 opCtx->recoveryUnit()->writing(&e->firstRecord)->Null();
537 opCtx->recoveryUnit()->writing(&e->lastRecord)->Null();
538
539
540 if (_getFreeListStart().isNull()) {
541 _setFreeListStart(opCtx, firstExt);
542 _setFreeListEnd(opCtx, firstExt);
543 } else {
544 DiskLoc a = _getFreeListStart();
545 invariant(getExtent(a)->xprev.isNull());
546 *opCtx->recoveryUnit()->writing(&getExtent(a)->xprev) = firstExt;
547 *opCtx->recoveryUnit()->writing(&getExtent(firstExt)->xnext) = a;
548 _setFreeListStart(opCtx, firstExt);
549 }
550 }
551
freeExtents(OperationContext * opCtx,DiskLoc firstExt,DiskLoc lastExt)552 void MmapV1ExtentManager::freeExtents(OperationContext* opCtx, DiskLoc firstExt, DiskLoc lastExt) {
553 Lock::ResourceLock rlk(opCtx->lockState(), _rid, MODE_X);
554
555 if (firstExt.isNull() && lastExt.isNull())
556 return;
557
558 {
559 verify(!firstExt.isNull() && !lastExt.isNull());
560 Extent* f = getExtent(firstExt);
561 Extent* l = getExtent(lastExt);
562 verify(f->xprev.isNull());
563 verify(l->xnext.isNull());
564 verify(f == l || !f->xnext.isNull());
565 verify(f == l || !l->xprev.isNull());
566 }
567
568 if (_getFreeListStart().isNull()) {
569 _setFreeListStart(opCtx, firstExt);
570 _setFreeListEnd(opCtx, lastExt);
571 } else {
572 DiskLoc a = _getFreeListStart();
573 invariant(getExtent(a)->xprev.isNull());
574 *opCtx->recoveryUnit()->writing(&getExtent(a)->xprev) = lastExt;
575 *opCtx->recoveryUnit()->writing(&getExtent(lastExt)->xnext) = a;
576 _setFreeListStart(opCtx, firstExt);
577 }
578 }
579
_getFreeListStart() const580 DiskLoc MmapV1ExtentManager::_getFreeListStart() const {
581 if (_files.empty())
582 return DiskLoc();
583 const DataFile* file = _getOpenFile(0);
584 return file->header()->freeListStart;
585 }
586
_getFreeListEnd() const587 DiskLoc MmapV1ExtentManager::_getFreeListEnd() const {
588 if (_files.empty())
589 return DiskLoc();
590 const DataFile* file = _getOpenFile(0);
591 return file->header()->freeListEnd;
592 }
593
_setFreeListStart(OperationContext * opCtx,DiskLoc loc)594 void MmapV1ExtentManager::_setFreeListStart(OperationContext* opCtx, DiskLoc loc) {
595 invariant(!_files.empty());
596 DataFile* file = _files[0];
597 *opCtx->recoveryUnit()->writing(&file->header()->freeListStart) = loc;
598 }
599
_setFreeListEnd(OperationContext * opCtx,DiskLoc loc)600 void MmapV1ExtentManager::_setFreeListEnd(OperationContext* opCtx, DiskLoc loc) {
601 invariant(!_files.empty());
602 DataFile* file = _files[0];
603 *opCtx->recoveryUnit()->writing(&file->header()->freeListEnd) = loc;
604 }
605
freeListStats(OperationContext * opCtx,int * numExtents,int64_t * totalFreeSizeBytes) const606 void MmapV1ExtentManager::freeListStats(OperationContext* opCtx,
607 int* numExtents,
608 int64_t* totalFreeSizeBytes) const {
609 Lock::ResourceLock rlk(opCtx->lockState(), _rid, MODE_S);
610
611 invariant(numExtents);
612 invariant(totalFreeSizeBytes);
613
614 *numExtents = 0;
615 *totalFreeSizeBytes = 0;
616
617 DiskLoc a = _getFreeListStart();
618 while (!a.isNull()) {
619 Extent* e = getExtent(a);
620 (*numExtents)++;
621 (*totalFreeSizeBytes) += e->length;
622 a = e->xnext;
623 }
624 }
625
626
627 namespace {
628 class CacheHintMadvise : public ExtentManager::CacheHint {
629 public:
CacheHintMadvise(void * p,unsigned len,MAdvise::Advice a)630 CacheHintMadvise(void* p, unsigned len, MAdvise::Advice a) : _advice(p, len, a) {}
631
632 private:
633 MAdvise _advice;
634 };
635 }
636
cacheHint(const DiskLoc & extentLoc,const ExtentManager::HintType & hint)637 ExtentManager::CacheHint* MmapV1ExtentManager::cacheHint(const DiskLoc& extentLoc,
638 const ExtentManager::HintType& hint) {
639 invariant(hint == Sequential);
640 Extent* e = getExtent(extentLoc);
641 return new CacheHintMadvise(reinterpret_cast<void*>(e), e->length, MAdvise::Sequential);
642 }
643
~FilesArray()644 MmapV1ExtentManager::FilesArray::~FilesArray() {
645 for (int i = 0; i < size(); i++) {
646 delete _files[i];
647 }
648 }
649
close(OperationContext * opCtx)650 void MmapV1ExtentManager::FilesArray::close(OperationContext* opCtx) {
651 for (int i = 0; i < size(); i++) {
652 _files[i]->close(opCtx);
653 }
654 }
655
push_back(DataFile * val)656 void MmapV1ExtentManager::FilesArray::push_back(DataFile* val) {
657 stdx::lock_guard<stdx::mutex> lk(_writersMutex);
658 const int n = _size.load();
659 invariant(n < DiskLoc::MaxFiles);
660 // Note ordering: _size update must come after updating the _files array
661 _files[n] = val;
662 _size.store(n + 1);
663 }
664
getFileFormat(OperationContext * opCtx) const665 DataFileVersion MmapV1ExtentManager::getFileFormat(OperationContext* opCtx) const {
666 if (numFiles() == 0)
667 return DataFileVersion(0, 0);
668
669 // We explicitly only look at the first file.
670 return _getOpenFile(0)->getHeader()->version;
671 }
672
setFileFormat(OperationContext * opCtx,DataFileVersion newVersion)673 void MmapV1ExtentManager::setFileFormat(OperationContext* opCtx, DataFileVersion newVersion) {
674 invariant(numFiles() > 0);
675
676 DataFile* df = _getOpenFile(0);
677 invariant(df);
678
679 *opCtx->recoveryUnit()->writing(&df->getHeader()->version) = newVersion;
680 }
681 }
682