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