From b2d3de3bf47b7eaf5bb6bf68de5946895bd943af Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 14 Mar 2013 18:34:37 +0000 Subject: Use mmap() to read from the database file in rollback mode. This branch is unix only for now. FossilOrigin-Name: 6f21d9cbf5d457e63a7282015a89ae785526cf6d --- src/os_unix.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/os_unix.c') diff --git a/src/os_unix.c b/src/os_unix.c index 8f094bdc1..89326783d 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -3623,6 +3623,10 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){ } return SQLITE_OK; } + case SQLITE_FCNTL_GETFD: { + *(int*)pArg = pFile->h; + return SQLITE_OK; + } #ifdef SQLITE_DEBUG /* The pager calls this method to signal that it has done ** a rollback and that the database is therefore unchanged and -- cgit v1.2.3 From 5d8a137218d43070a2c635900d1ae7d7a7ef0f69 Mon Sep 17 00:00:00 2001 From: dan Date: Tue, 19 Mar 2013 19:28:06 +0000 Subject: Add the sqlite3_io_methods.xMremap() method to the VFS interface. Also "PRAGMA mmap_size". FossilOrigin-Name: 6183f1bd86ceed76d22d9762f3d7eb33262c62d1 --- src/os_unix.c | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) (limited to 'src/os_unix.c') diff --git a/src/os_unix.c b/src/os_unix.c index 89326783d..f37f2404e 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -4429,6 +4429,42 @@ static int unixShmUnmap( # define unixShmUnmap 0 #endif /* #ifndef SQLITE_OMIT_WAL */ +/* +** Map, remap or unmap part of the database file. +*/ +static int unixMremap( + sqlite3_file *fd, /* Main database file */ + sqlite3_int64 iOff, /* Offset to start mapping at */ + sqlite3_int64 nOld, /* Size of old mapping, or zero */ + sqlite3_int64 nNew, /* Size of new mapping, or zero */ + void **ppMap /* IN/OUT: Old/new mappings */ +){ + unixFile *p = (unixFile *)fd; /* The underlying database file */ + int rc = SQLITE_OK; /* Return code */ + void *pNew = 0; /* New mapping */ + + assert( iOff==0 ); + + if( nOld!=0 ){ + void *pOld = *ppMap; + munmap(pOld, nOld); + } + + if( nNew>0 ){ + int flags = PROT_READ; + if( (p->ctrlFlags & UNIXFILE_RDONLY)==0 ) flags |= PROT_WRITE; + nNew = (nNew+4095) & ~(i64)((1 << 12)-1); + pNew = mmap(0, nNew, flags, MAP_SHARED, p->h, iOff); + if( pNew==MAP_FAILED ){ + pNew = 0; + rc = SQLITE_IOERR; + } + } + + *ppMap = pNew; + return rc; +} + /* ** Here ends the implementation of all sqlite3_file methods. ** @@ -4487,7 +4523,8 @@ static const sqlite3_io_methods METHOD = { \ unixShmMap, /* xShmMap */ \ unixShmLock, /* xShmLock */ \ unixShmBarrier, /* xShmBarrier */ \ - unixShmUnmap /* xShmUnmap */ \ + unixShmUnmap, /* xShmUnmap */ \ + unixMremap, /* xMremap */ \ }; \ static const sqlite3_io_methods *FINDER##Impl(const char *z, unixFile *p){ \ UNUSED_PARAMETER(z); UNUSED_PARAMETER(p); \ @@ -4504,7 +4541,7 @@ static const sqlite3_io_methods *(*const FINDER)(const char*,unixFile *p) \ IOMETHODS( posixIoFinder, /* Finder function name */ posixIoMethods, /* sqlite3_io_methods object name */ - 2, /* shared memory is enabled */ + 3, /* shared memory and mmap are enabled */ unixClose, /* xClose method */ unixLock, /* xLock method */ unixUnlock, /* xUnlock method */ -- cgit v1.2.3 From eb97b29345592cf094b6639a985f9253653a5063 Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 20 Mar 2013 14:26:59 +0000 Subject: When possible, use memory mapping when appending new pages to a database file. FossilOrigin-Name: 14135da3cdbafd699563a29608f32347cda28338 --- src/os_unix.c | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) (limited to 'src/os_unix.c') diff --git a/src/os_unix.c b/src/os_unix.c index f37f2404e..1c26c6bad 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -4434,6 +4434,7 @@ static int unixShmUnmap( */ static int unixMremap( sqlite3_file *fd, /* Main database file */ + int flags, /* Mask of SQLITE_MREMAP_XXX flags */ sqlite3_int64 iOff, /* Offset to start mapping at */ sqlite3_int64 nOld, /* Size of old mapping, or zero */ sqlite3_int64 nNew, /* Size of new mapping, or zero */ @@ -4442,8 +4443,35 @@ static int unixMremap( unixFile *p = (unixFile *)fd; /* The underlying database file */ int rc = SQLITE_OK; /* Return code */ void *pNew = 0; /* New mapping */ + i64 nRnd; /* nNew rounded up to 4096 */ assert( iOff==0 ); + nRnd = (nNew+4095) & ~(i64)((1 << 12)-1); + + /* If the SQLITE_MREMAP_EXTEND flag is set, then the size of the requested + ** mapping (nNew bytes) may be greater than the size of the database file. + ** If this is the case, extend the file on disk using ftruncate(). */ + assert( nNew>0 || (flags & SQLITE_MREMAP_EXTEND)==0 ); + if( flags & SQLITE_MREMAP_EXTEND ){ + struct stat statbuf; /* Low-level file information */ + rc = osFstat(p->h, &statbuf); + if( rc==SQLITE_OK && nNew>statbuf.st_size ){ + rc = robust_ftruncate(p->h, nNew); + } + if( rc!=SQLITE_OK ) return rc; + } + +#if defined(_GNU_SOURCE) && defined(__linux__) + if( nRnd && nOld ){ + void *pOld = *ppMap; + *ppMap = pNew = mremap(pOld, nOld, nNew, MREMAP_MAYMOVE); + if( pNew==MAP_FAILED ){ + *ppMap = 0; + return SQLITE_IOERR_MREMAP; + } + return SQLITE_OK; + } +#endif if( nOld!=0 ){ void *pOld = *ppMap; @@ -4453,11 +4481,10 @@ static int unixMremap( if( nNew>0 ){ int flags = PROT_READ; if( (p->ctrlFlags & UNIXFILE_RDONLY)==0 ) flags |= PROT_WRITE; - nNew = (nNew+4095) & ~(i64)((1 << 12)-1); - pNew = mmap(0, nNew, flags, MAP_SHARED, p->h, iOff); + pNew = mmap(0, nRnd, flags, MAP_SHARED, p->h, iOff); if( pNew==MAP_FAILED ){ pNew = 0; - rc = SQLITE_IOERR; + rc = SQLITE_IOERR_MREMAP; } } -- cgit v1.2.3 From d306e1a3a113bd3905c6b661bcb8176b0bb6a844 Mon Sep 17 00:00:00 2001 From: dan Date: Wed, 20 Mar 2013 18:25:49 +0000 Subject: Optimize the xMremap method in os_unix.c some. FossilOrigin-Name: 9529ed88a71fee02fae72dc86f0669bd6856ff92 --- src/os_unix.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 53 insertions(+), 8 deletions(-) (limited to 'src/os_unix.c') diff --git a/src/os_unix.c b/src/os_unix.c index 1c26c6bad..7ab61ed0b 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -4429,6 +4429,16 @@ static int unixShmUnmap( # define unixShmUnmap 0 #endif /* #ifndef SQLITE_OMIT_WAL */ +/* +** Arguments x and y are both integers. Argument y must be a power of 2. +** Round x up to the nearest integer multiple of y. For example: +** +** ROUNDUP(0, 8) -> 0 +** ROUNDUP(13, 8) -> 16 +** ROUNDUP(32, 8) -> 32 +*/ +#define ROUNDUP(x,y) (((x)+y-1)&~(y-1)) + /* ** Map, remap or unmap part of the database file. */ @@ -4443,10 +4453,10 @@ static int unixMremap( unixFile *p = (unixFile *)fd; /* The underlying database file */ int rc = SQLITE_OK; /* Return code */ void *pNew = 0; /* New mapping */ - i64 nRnd; /* nNew rounded up to 4096 */ + i64 nNewRnd; /* nNew rounded up */ + i64 nOldRnd; /* nOld rounded up */ assert( iOff==0 ); - nRnd = (nNew+4095) & ~(i64)((1 << 12)-1); /* If the SQLITE_MREMAP_EXTEND flag is set, then the size of the requested ** mapping (nNew bytes) may be greater than the size of the database file. @@ -4461,10 +4471,43 @@ static int unixMremap( if( rc!=SQLITE_OK ) return rc; } + /* According to some sources, the effect of changing the size of the + ** underlying file on mapped regions that correspond to the added or + ** removed pages is undefined. However, there is reason to believe that + ** on modern platforms like Linux or OSX, things just work. For example, + ** it is possible to create a mapping larger than the file on disk and + ** extend the file on disk later on. + ** + ** Exploit this on OSX to reduce the number of munmap()/mmap() calls + ** if the file size is changing. In this case all mappings are rounded + ** up to the nearest 4MB. And if a new mapping is requested that has the + ** same rounded size as an old mapping, the old mapping can simply be + ** reused as is. + ** + ** It would be possible to do the above on Linux too. However, Linux has + ** the non-standard mremap() call to resize existing mappings, which can + ** be used instead. */ +#if defined(__APPLE__) + nNewRnd = ROUNDUP(nNew, 4096*1024); + nOldRnd = ROUNDUP(nOld, 4096*1024); +#else + nNewRnd = ROUNDUP(nNew, 4096*1); + nOldRnd = ROUNDUP(nOld, 4096*1); +#endif + + /* On OSX or Linux, reuse the old mapping if it is the right size. */ +#if defined(__APPLE__) || defined(__linux__) + if( nNewRnd==nOldRnd ){ + return SQLITE_OK; + } +#endif + + /* On Linux, if there is both an old and new mapping, resize the old + ** mapping using the non-standard mremap() call. */ #if defined(_GNU_SOURCE) && defined(__linux__) - if( nRnd && nOld ){ + if( nNewRnd && nOldRnd ){ void *pOld = *ppMap; - *ppMap = pNew = mremap(pOld, nOld, nNew, MREMAP_MAYMOVE); + *ppMap = pNew = mremap(pOld, nOldRnd, nNewRnd, MREMAP_MAYMOVE); if( pNew==MAP_FAILED ){ *ppMap = 0; return SQLITE_IOERR_MREMAP; @@ -4473,15 +4516,17 @@ static int unixMremap( } #endif - if( nOld!=0 ){ + /* If we get this far, unmap any old mapping. */ + if( nOldRnd!=0 ){ void *pOld = *ppMap; - munmap(pOld, nOld); + munmap(pOld, nOldRnd); } - if( nNew>0 ){ + /* And, if required, use mmap() to create a new mapping. */ + if( nNewRnd>0 ){ int flags = PROT_READ; if( (p->ctrlFlags & UNIXFILE_RDONLY)==0 ) flags |= PROT_WRITE; - pNew = mmap(0, nRnd, flags, MAP_SHARED, p->h, iOff); + pNew = mmap(0, nNewRnd, flags, MAP_SHARED, p->h, iOff); if( pNew==MAP_FAILED ){ pNew = 0; rc = SQLITE_IOERR_MREMAP; -- cgit v1.2.3 From c71b45e61944f6964b384614f3da737eff2c2dd6 Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 21 Mar 2013 14:47:47 +0000 Subject: Do not use the Linux mremap() call. Use the same strategy for xMremap() as on OSX instead. FossilOrigin-Name: 5ed8ad780c991d2ca44003ee84350fb5e95ad58e --- src/os_unix.c | 30 ++++++------------------------ 1 file changed, 6 insertions(+), 24 deletions(-) (limited to 'src/os_unix.c') diff --git a/src/os_unix.c b/src/os_unix.c index 7ab61ed0b..153cf5f43 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -4478,16 +4478,12 @@ static int unixMremap( ** it is possible to create a mapping larger than the file on disk and ** extend the file on disk later on. ** - ** Exploit this on OSX to reduce the number of munmap()/mmap() calls - ** if the file size is changing. In this case all mappings are rounded - ** up to the nearest 4MB. And if a new mapping is requested that has the - ** same rounded size as an old mapping, the old mapping can simply be - ** reused as is. - ** - ** It would be possible to do the above on Linux too. However, Linux has - ** the non-standard mremap() call to resize existing mappings, which can - ** be used instead. */ -#if defined(__APPLE__) + ** Exploit this on Linux and OSX to reduce the number of munmap()/mmap() + ** calls required if the file size is changing. In this case all mappings + ** are rounded up to the nearest 4MB. And if a new mapping is requested + ** that has the same rounded size as an old mapping, the old mapping can + ** be reused as is. */ +#if defined(__APPLE__) || defined(__linux__) nNewRnd = ROUNDUP(nNew, 4096*1024); nOldRnd = ROUNDUP(nOld, 4096*1024); #else @@ -4502,20 +4498,6 @@ static int unixMremap( } #endif - /* On Linux, if there is both an old and new mapping, resize the old - ** mapping using the non-standard mremap() call. */ -#if defined(_GNU_SOURCE) && defined(__linux__) - if( nNewRnd && nOldRnd ){ - void *pOld = *ppMap; - *ppMap = pNew = mremap(pOld, nOldRnd, nNewRnd, MREMAP_MAYMOVE); - if( pNew==MAP_FAILED ){ - *ppMap = 0; - return SQLITE_IOERR_MREMAP; - } - return SQLITE_OK; - } -#endif - /* If we get this far, unmap any old mapping. */ if( nOldRnd!=0 ){ void *pOld = *ppMap; -- cgit v1.2.3 From 6101d50471a97423032fbffcb72f5b62a28a90ee Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 22 Mar 2013 08:58:38 +0000 Subject: Add assert statements to os_unix.c to ensure that any mapped region of the database file is not being read or written using the xRead() or xWrite() methods. FossilOrigin-Name: 765615f9fba7c1765eb741cb98a09a28b464ee55 --- src/os_unix.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'src/os_unix.c') diff --git a/src/os_unix.c b/src/os_unix.c index 153cf5f43..88f317702 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -249,6 +249,8 @@ struct unixFile { unsigned char transCntrChng; /* True if the transaction counter changed */ unsigned char dbUpdate; /* True if any part of database file changed */ unsigned char inNormalWrite; /* True if in a normal write operation */ + sqlite3_int64 mmapSize; /* Size of xMremap() */ + void *pMapRegion; /* Area memory mapped */ #endif #ifdef SQLITE_TEST /* In test mode, increase the size of this structure a bit so that @@ -3072,6 +3074,7 @@ static int unixRead( unixFile *pFile = (unixFile *)id; int got; assert( id ); + assert( offset>=pFile->mmapSize ); /* Never read from the mmapped region */ /* If this is a database file (not a journal, master-journal or temp ** file), the bytes in the locking range should never be read or written. */ @@ -3154,6 +3157,7 @@ static int unixWrite( int wrote = 0; assert( id ); assert( amt>0 ); + assert( offset>=pFile->mmapSize ); /* Never write into the mmapped region */ /* If this is a database file (not a journal, master-journal or temp ** file), the bytes in the locking range should never be read or written. */ @@ -4457,6 +4461,8 @@ static int unixMremap( i64 nOldRnd; /* nOld rounded up */ assert( iOff==0 ); + assert( p->mmapSize==nOld ); + assert( p->pMapRegion==0 || p->pMapRegion==(*ppMap) ); /* If the SQLITE_MREMAP_EXTEND flag is set, then the size of the requested ** mapping (nNew bytes) may be greater than the size of the database file. @@ -4494,6 +4500,7 @@ static int unixMremap( /* On OSX or Linux, reuse the old mapping if it is the right size. */ #if defined(__APPLE__) || defined(__linux__) if( nNewRnd==nOldRnd ){ + VVA_ONLY( p->mmapSize = nNew; ) return SQLITE_OK; } #endif @@ -4502,6 +4509,7 @@ static int unixMremap( if( nOldRnd!=0 ){ void *pOld = *ppMap; munmap(pOld, nOldRnd); + VVA_ONLY( p->mmapSize = 0; p->pMapRegion = 0; ); } /* And, if required, use mmap() to create a new mapping. */ @@ -4511,7 +4519,10 @@ static int unixMremap( pNew = mmap(0, nNewRnd, flags, MAP_SHARED, p->h, iOff); if( pNew==MAP_FAILED ){ pNew = 0; + VVA_ONLY( p->mmapSize = 0; p->pMapRegion = 0; ) rc = SQLITE_IOERR_MREMAP; + }else{ + VVA_ONLY( p->mmapSize = nNew; p->pMapRegion = pNew; ) } } @@ -4846,6 +4857,7 @@ static int fillInUnixFile( pNew->pVfs = pVfs; pNew->zPath = zFilename; pNew->ctrlFlags = (u8)ctrlFlags; + VVA_ONLY( pNew->mmapSize = 0; ) if( sqlite3_uri_boolean(((ctrlFlags & UNIXFILE_URI) ? zFilename : 0), "psow", SQLITE_POWERSAFE_OVERWRITE) ){ pNew->ctrlFlags |= UNIXFILE_PSOW; -- cgit v1.2.3 From c00033125d612de5877ea2682cd255dd3199c00c Mon Sep 17 00:00:00 2001 From: dan Date: Fri, 22 Mar 2013 17:46:11 +0000 Subject: Add a fix for the assert() statements added by the previous commit. FossilOrigin-Name: 19345416ed5e1ab5b0b35993b0b9069c2fb1683b --- src/os_unix.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'src/os_unix.c') diff --git a/src/os_unix.c b/src/os_unix.c index 88f317702..2f03c3967 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -3470,6 +3470,14 @@ static int unixTruncate(sqlite3_file *id, i64 nByte){ if( pFile->inNormalWrite && nByte==0 ){ pFile->transCntrChng = 1; } + + /* If the file was just truncated to a size smaller than the currently + ** mapped region, reduce the effective mapping size as well. SQLite will + ** use read() and write() to access data beyond this point from now on. + */ + if( nBytemmapSize ){ + pFile->mmapSize = nByte; + } #endif return SQLITE_OK; @@ -4461,7 +4469,7 @@ static int unixMremap( i64 nOldRnd; /* nOld rounded up */ assert( iOff==0 ); - assert( p->mmapSize==nOld ); + /* assert( p->mmapSize==nOld ); */ assert( p->pMapRegion==0 || p->pMapRegion==(*ppMap) ); /* If the SQLITE_MREMAP_EXTEND flag is set, then the size of the requested -- cgit v1.2.3 From f23da96636f9de1a81c939bd7a0cc25ee1d37f49 Mon Sep 17 00:00:00 2001 From: dan Date: Sat, 23 Mar 2013 21:00:41 +0000 Subject: Replace the sqlite3_io_methods.xMremap interface with sqlite3_io_methods.xFetch and xUnfetch. FossilOrigin-Name: 1431be95579160fb70408d43e17fc23c7b69ab4a --- src/os_unix.c | 213 +++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 129 insertions(+), 84 deletions(-) (limited to 'src/os_unix.c') diff --git a/src/os_unix.c b/src/os_unix.c index 2f03c3967..334719200 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -249,9 +249,14 @@ struct unixFile { unsigned char transCntrChng; /* True if the transaction counter changed */ unsigned char dbUpdate; /* True if any part of database file changed */ unsigned char inNormalWrite; /* True if in a normal write operation */ - sqlite3_int64 mmapSize; /* Size of xMremap() */ - void *pMapRegion; /* Area memory mapped */ + #endif + sqlite3_int64 mmapSize; /* Usable size of mapping at pMapRegion */ + sqlite3_int64 mmapOrigsize; /* Actual size of mapping at pMapRegion */ + sqlite3_int64 mmapLimit; /* Configured FCNTL_MMAP_SIZE value */ + void *pMapRegion; /* Memory mapped region */ + int nFetchOut; /* Number of outstanding xFetch refs */ + #ifdef SQLITE_TEST /* In test mode, increase the size of this structure a bit so that ** it is larger than the struct CrashFile defined in test6.c. @@ -1805,6 +1810,9 @@ static int unixUnlock(sqlite3_file *id, int eFileLock){ return posixUnlock(id, eFileLock, 0); } +static int unixMapfile(unixFile *pFd, i64 nByte); +static void unixUnmapfile(unixFile *pFd); + /* ** This function performs the parts of the "close file" operation ** common to all locking schemes. It closes the directory and file @@ -1817,6 +1825,7 @@ static int unixUnlock(sqlite3_file *id, int eFileLock){ */ static int closeUnixFile(sqlite3_file *id){ unixFile *pFile = (unixFile*)id; + unixUnmapfile(pFile); if( pFile->h>=0 ){ robust_close(pFile, pFile->h, __LINE__); pFile->h = -1; @@ -3074,7 +3083,6 @@ static int unixRead( unixFile *pFile = (unixFile *)id; int got; assert( id ); - assert( offset>=pFile->mmapSize ); /* Never read from the mmapped region */ /* If this is a database file (not a journal, master-journal or temp ** file), the bytes in the locking range should never be read or written. */ @@ -3085,6 +3093,21 @@ static int unixRead( ); #endif + /* Deal with as much of this write request as possible by transfering + ** data to the memory mapping using memcpy(). */ + if( offsetmmapSize ){ + if( offset+amt <= pFile->mmapSize ){ + memcpy(pBuf, &((u8 *)(pFile->pMapRegion))[offset], amt); + return SQLITE_OK; + }else{ + int nCopy = pFile->mmapSize - offset; + memcpy(pBuf, &((u8 *)(pFile->pMapRegion))[offset], nCopy); + pBuf = &((u8 *)pBuf)[nCopy]; + amt -= nCopy; + offset += nCopy; + } + } + got = seekAndRead(pFile, offset, pBuf, amt); if( got==amt ){ return SQLITE_OK; @@ -3157,7 +3180,6 @@ static int unixWrite( int wrote = 0; assert( id ); assert( amt>0 ); - assert( offset>=pFile->mmapSize ); /* Never write into the mmapped region */ /* If this is a database file (not a journal, master-journal or temp ** file), the bytes in the locking range should never be read or written. */ @@ -3190,6 +3212,21 @@ static int unixWrite( } #endif + /* Deal with as much of this write request as possible by transfering + ** data from the memory mapping using memcpy(). */ + if( offsetmmapSize ){ + if( offset+amt <= pFile->mmapSize ){ + memcpy(&((u8 *)(pFile->pMapRegion))[offset], pBuf, amt); + return SQLITE_OK; + }else{ + int nCopy = pFile->mmapSize - offset; + memcpy(&((u8 *)(pFile->pMapRegion))[offset], pBuf, nCopy); + pBuf = &((u8 *)pBuf)[nCopy]; + amt -= nCopy; + offset += nCopy; + } + } + while( amt>0 && (wrote = seekAndWrite(pFile, offset, pBuf, amt))>0 ){ amt -= wrote; offset += wrote; @@ -3470,6 +3507,7 @@ static int unixTruncate(sqlite3_file *id, i64 nByte){ if( pFile->inNormalWrite && nByte==0 ){ pFile->transCntrChng = 1; } +#endif /* If the file was just truncated to a size smaller than the currently ** mapped region, reduce the effective mapping size as well. SQLite will @@ -3478,7 +3516,6 @@ static int unixTruncate(sqlite3_file *id, i64 nByte){ if( nBytemmapSize ){ pFile->mmapSize = nByte; } -#endif return SQLITE_OK; } @@ -3568,6 +3605,19 @@ static int fcntlSizeHint(unixFile *pFile, i64 nByte){ } } + if( pFile->mmapLimit>0 ){ + int rc; + if( pFile->szChunk<=0 ){ + if( robust_ftruncate(pFile->h, nByte) ){ + pFile->lastErrno = errno; + return unixLogError(SQLITE_IOERR_TRUNCATE, "ftruncate", pFile->zPath); + } + } + + rc = unixMapfile(pFile, nByte); + return rc; + } + return SQLITE_OK; } @@ -3635,8 +3685,8 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){ } return SQLITE_OK; } - case SQLITE_FCNTL_GETFD: { - *(int*)pArg = pFile->h; + case SQLITE_FCNTL_MMAP_SIZE: { + pFile->mmapLimit = *(i64*)pArg; return SQLITE_OK; } #ifdef SQLITE_DEBUG @@ -4451,91 +4501,86 @@ static int unixShmUnmap( */ #define ROUNDUP(x,y) (((x)+y-1)&~(y-1)) -/* -** Map, remap or unmap part of the database file. -*/ -static int unixMremap( - sqlite3_file *fd, /* Main database file */ - int flags, /* Mask of SQLITE_MREMAP_XXX flags */ - sqlite3_int64 iOff, /* Offset to start mapping at */ - sqlite3_int64 nOld, /* Size of old mapping, or zero */ - sqlite3_int64 nNew, /* Size of new mapping, or zero */ - void **ppMap /* IN/OUT: Old/new mappings */ -){ - unixFile *p = (unixFile *)fd; /* The underlying database file */ - int rc = SQLITE_OK; /* Return code */ - void *pNew = 0; /* New mapping */ - i64 nNewRnd; /* nNew rounded up */ - i64 nOldRnd; /* nOld rounded up */ - - assert( iOff==0 ); - /* assert( p->mmapSize==nOld ); */ - assert( p->pMapRegion==0 || p->pMapRegion==(*ppMap) ); - - /* If the SQLITE_MREMAP_EXTEND flag is set, then the size of the requested - ** mapping (nNew bytes) may be greater than the size of the database file. - ** If this is the case, extend the file on disk using ftruncate(). */ - assert( nNew>0 || (flags & SQLITE_MREMAP_EXTEND)==0 ); - if( flags & SQLITE_MREMAP_EXTEND ){ +static void unixUnmapfile(unixFile *pFd){ + assert( pFd->nFetchOut==0 ); + if( pFd->pMapRegion ){ + munmap(pFd->pMapRegion, pFd->mmapOrigsize); + pFd->pMapRegion = 0; + pFd->mmapSize = 0; + pFd->mmapOrigsize = 0; + } +} + +static int unixMapfile(unixFile *pFd, i64 nByte){ + i64 nMap = nByte; + int rc; + + assert( nMap>=0 || pFd->nFetchOut==0 ); + if( pFd->nFetchOut>0 ) return SQLITE_OK; + + if( nMap<0 ){ struct stat statbuf; /* Low-level file information */ - rc = osFstat(p->h, &statbuf); - if( rc==SQLITE_OK && nNew>statbuf.st_size ){ - rc = robust_ftruncate(p->h, nNew); + rc = osFstat(pFd->h, &statbuf); + if( rc!=SQLITE_OK ){ + return SQLITE_IOERR_FSTAT; } - if( rc!=SQLITE_OK ) return rc; + nMap = statbuf.st_size; + } + if( nMap>pFd->mmapLimit ){ + nMap = pFd->mmapLimit; } - /* According to some sources, the effect of changing the size of the - ** underlying file on mapped regions that correspond to the added or - ** removed pages is undefined. However, there is reason to believe that - ** on modern platforms like Linux or OSX, things just work. For example, - ** it is possible to create a mapping larger than the file on disk and - ** extend the file on disk later on. - ** - ** Exploit this on Linux and OSX to reduce the number of munmap()/mmap() - ** calls required if the file size is changing. In this case all mappings - ** are rounded up to the nearest 4MB. And if a new mapping is requested - ** that has the same rounded size as an old mapping, the old mapping can - ** be reused as is. */ -#if defined(__APPLE__) || defined(__linux__) - nNewRnd = ROUNDUP(nNew, 4096*1024); - nOldRnd = ROUNDUP(nOld, 4096*1024); -#else - nNewRnd = ROUNDUP(nNew, 4096*1); - nOldRnd = ROUNDUP(nOld, 4096*1); -#endif + if( nMap!=pFd->mmapSize ){ + void *pNew; + unixUnmapfile(pFd); - /* On OSX or Linux, reuse the old mapping if it is the right size. */ -#if defined(__APPLE__) || defined(__linux__) - if( nNewRnd==nOldRnd ){ - VVA_ONLY( p->mmapSize = nNew; ) - return SQLITE_OK; + if( nMap>0 ){ + void *pNew; + int flags = PROT_READ; + if( (pFd->ctrlFlags & UNIXFILE_RDONLY)==0 ) flags |= PROT_WRITE; + pNew = mmap(0, ROUNDUP(nMap, 4096), flags, MAP_SHARED, pFd->h, 0); + if( pNew==MAP_FAILED ){ + return SQLITE_IOERR_MREMAP; + } + + pFd->pMapRegion = pNew; + pFd->mmapOrigsize = pFd->mmapSize = nMap; + } } -#endif - /* If we get this far, unmap any old mapping. */ - if( nOldRnd!=0 ){ - void *pOld = *ppMap; - munmap(pOld, nOldRnd); - VVA_ONLY( p->mmapSize = 0; p->pMapRegion = 0; ); - } - - /* And, if required, use mmap() to create a new mapping. */ - if( nNewRnd>0 ){ - int flags = PROT_READ; - if( (p->ctrlFlags & UNIXFILE_RDONLY)==0 ) flags |= PROT_WRITE; - pNew = mmap(0, nNewRnd, flags, MAP_SHARED, p->h, iOff); - if( pNew==MAP_FAILED ){ - pNew = 0; - VVA_ONLY( p->mmapSize = 0; p->pMapRegion = 0; ) - rc = SQLITE_IOERR_MREMAP; - }else{ - VVA_ONLY( p->mmapSize = nNew; p->pMapRegion = pNew; ) + return SQLITE_OK; +} + +static int unixFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){ + unixFile *pFd = (unixFile *)fd; /* The underlying database file */ + *pp = 0; + + if( pFd->mmapLimit>0 ){ + if( pFd->pMapRegion==0 ){ + int rc = unixMapfile(pFd, -1); + if( rc!=SQLITE_OK ) return rc; } + if( pFd->mmapSize >= iOff+nAmt ){ + *pp = &((u8 *)pFd->pMapRegion)[iOff]; + pFd->nFetchOut++; + } + } + return SQLITE_OK; +} + +static int unixUnfetch(sqlite3_file *fd, void *p){ + unixFile *pFd = (unixFile *)fd; /* The underlying database file */ + + assert( (p==0)==(pFd->nFetchOut==0) ); + + if( p ){ + pFd->nFetchOut--; + }else{ + unixUnmapfile(pFd); } - *ppMap = pNew; - return rc; + assert( pFd->nFetchOut>=0 ); + return SQLITE_OK; } /* @@ -4597,7 +4642,8 @@ static const sqlite3_io_methods METHOD = { \ unixShmLock, /* xShmLock */ \ unixShmBarrier, /* xShmBarrier */ \ unixShmUnmap, /* xShmUnmap */ \ - unixMremap, /* xMremap */ \ + unixFetch, /* xFetch */ \ + unixUnfetch, /* xUnfetch */ \ }; \ static const sqlite3_io_methods *FINDER##Impl(const char *z, unixFile *p){ \ UNUSED_PARAMETER(z); UNUSED_PARAMETER(p); \ @@ -4865,7 +4911,6 @@ static int fillInUnixFile( pNew->pVfs = pVfs; pNew->zPath = zFilename; pNew->ctrlFlags = (u8)ctrlFlags; - VVA_ONLY( pNew->mmapSize = 0; ) if( sqlite3_uri_boolean(((ctrlFlags & UNIXFILE_URI) ? zFilename : 0), "psow", SQLITE_POWERSAFE_OVERWRITE) ){ pNew->ctrlFlags |= UNIXFILE_PSOW; -- cgit v1.2.3 From a1afc7425a42b774b307416b3256d28c1d4a185f Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 25 Mar 2013 13:50:49 +0000 Subject: Fix a case in the pager where an xFetch() reference was being leaked following an OOM error. FossilOrigin-Name: 5885ba6ce768658ec25b60747430d147b315b55c --- src/os_unix.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/os_unix.c') diff --git a/src/os_unix.c b/src/os_unix.c index 334719200..d86581057 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -1807,6 +1807,7 @@ end_unlock: ** the requested locking level, this routine is a no-op. */ static int unixUnlock(sqlite3_file *id, int eFileLock){ + assert( eFileLock==SHARED_LOCK || ((unixFile *)id)->nFetchOut==0 ); return posixUnlock(id, eFileLock, 0); } @@ -4531,7 +4532,6 @@ static int unixMapfile(unixFile *pFd, i64 nByte){ } if( nMap!=pFd->mmapSize ){ - void *pNew; unixUnmapfile(pFd); if( nMap>0 ){ -- cgit v1.2.3 From aef49d7141d9bbbc68a2c8ce7bd17cdafb06aa6b Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 25 Mar 2013 16:28:54 +0000 Subject: Remove unnecessary code to round the size of a memory mapping to 4KB from os_unix.c. Rename SQLITE_IOERR_MREMAP to SQLITE_IOERR_MMAP. Fix other small issues in os_unix.c. FossilOrigin-Name: dce35c01a5fe66d2970075b1e3f0376026485e4c --- src/os_unix.c | 54 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 43 insertions(+), 11 deletions(-) (limited to 'src/os_unix.c') diff --git a/src/os_unix.c b/src/os_unix.c index d86581057..a97aba87f 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -4493,15 +4493,8 @@ static int unixShmUnmap( #endif /* #ifndef SQLITE_OMIT_WAL */ /* -** Arguments x and y are both integers. Argument y must be a power of 2. -** Round x up to the nearest integer multiple of y. For example: -** -** ROUNDUP(0, 8) -> 0 -** ROUNDUP(13, 8) -> 16 -** ROUNDUP(32, 8) -> 32 +** If it is currently memory mapped, unmap file pFd. */ -#define ROUNDUP(x,y) (((x)+y-1)&~(y-1)) - static void unixUnmapfile(unixFile *pFd){ assert( pFd->nFetchOut==0 ); if( pFd->pMapRegion ){ @@ -4512,6 +4505,22 @@ static void unixUnmapfile(unixFile *pFd){ } } +/* +** Memory map or remap the file opened by file-descriptor pFd (if the file +** is already mapped, the existing mapping is replaced by the new). Or, if +** there already exists a mapping for this file, and there are still +** outstanding xFetch() references to it, this function is a no-op. +** +** If parameter nByte is non-negative, then it is the requested size of +** the mapping to create. Otherwise, if nByte is less than zero, then the +** requested size is the size of the file on disk. The actual size of the +** created mapping is either the requested size or the value configured +** using SQLITE_FCNTL_MMAP_SIZE, whichever is smaller. +** +** SQLITE_OK is returned if no error occurs (even if the mapping is not +** recreated as a result of outstanding references) or an SQLite error +** code otherwise. +*/ static int unixMapfile(unixFile *pFd, i64 nByte){ i64 nMap = nByte; int rc; @@ -4538,19 +4547,32 @@ static int unixMapfile(unixFile *pFd, i64 nByte){ void *pNew; int flags = PROT_READ; if( (pFd->ctrlFlags & UNIXFILE_RDONLY)==0 ) flags |= PROT_WRITE; - pNew = mmap(0, ROUNDUP(nMap, 4096), flags, MAP_SHARED, pFd->h, 0); + pNew = mmap(0, nMap, flags, MAP_SHARED, pFd->h, 0); if( pNew==MAP_FAILED ){ - return SQLITE_IOERR_MREMAP; + return SQLITE_IOERR_MMAP; } pFd->pMapRegion = pNew; - pFd->mmapOrigsize = pFd->mmapSize = nMap; + pFd->mmapSize = nMap; + pFd->mmapOrigsize = nMap; } } return SQLITE_OK; } +/* +** If possible, return a pointer to a mapping of file fd starting at offset +** iOff. The mapping must be valid for at least nAmt bytes. +** +** If such a pointer can be obtained, store it in *pp and return SQLITE_OK. +** Or, if one cannot but no error occurs, set *pp to 0 and return SQLITE_OK. +** Finally, if an error does occur, return an SQLite error code. The final +** value of *pp is undefined in this case. +** +** If this function does return a pointer, the caller must eventually +** release the reference by calling unixUnfetch(). +*/ static int unixFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){ unixFile *pFd = (unixFile *)fd; /* The underlying database file */ *pp = 0; @@ -4568,9 +4590,19 @@ static int unixFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){ return SQLITE_OK; } +/* +** If the second argument is non-NULL, then this function releases a +** reference obtained by an earlier call to unixFetch(). Or, if the second +** argument is NULL, then this function is being called to inform the VFS +** layer that, according to POSIX, any existing mapping may now be invalid +** and should be unmapped. +*/ static int unixUnfetch(sqlite3_file *fd, void *p){ unixFile *pFd = (unixFile *)fd; /* The underlying database file */ + /* If p==0 (unmap the entire file) then there must be no outstanding + ** xFetch references. Or, if p!=0 (meaning it is an xFetch reference), + ** then there must be at least one outstanding. */ assert( (p==0)==(pFd->nFetchOut==0) ); if( p ){ -- cgit v1.2.3 From df737fe6f51522c478235ff6ddc478243f718bf2 Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 25 Mar 2013 17:00:24 +0000 Subject: Change the signature of the xUnfetch method to "int (*xUnfetch)(sqlite3_file*, sqlite3_int64 iOfst, void *p)". FossilOrigin-Name: 115b830509e8f0aa9d5965c1e9cd4f2ed9d01938 --- src/os_unix.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'src/os_unix.c') diff --git a/src/os_unix.c b/src/os_unix.c index a97aba87f..00c0088f8 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -4591,13 +4591,16 @@ static int unixFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){ } /* -** If the second argument is non-NULL, then this function releases a -** reference obtained by an earlier call to unixFetch(). Or, if the second -** argument is NULL, then this function is being called to inform the VFS -** layer that, according to POSIX, any existing mapping may now be invalid -** and should be unmapped. +** If the third argument is non-NULL, then this function releases a +** reference obtained by an earlier call to unixFetch(). The second +** argument passed to this function must be the same as the corresponding +** argument that was passed to the unixFetch() invocation. +** +** Or, if the third argument is NULL, then this function is being called +** to inform the VFS layer that, according to POSIX, any existing mapping +** may now be invalid and should be unmapped. */ -static int unixUnfetch(sqlite3_file *fd, void *p){ +static int unixUnfetch(sqlite3_file *fd, i64 iOff, void *p){ unixFile *pFd = (unixFile *)fd; /* The underlying database file */ /* If p==0 (unmap the entire file) then there must be no outstanding @@ -4605,6 +4608,9 @@ static int unixUnfetch(sqlite3_file *fd, void *p){ ** then there must be at least one outstanding. */ assert( (p==0)==(pFd->nFetchOut==0) ); + /* If p!=0, it must match the iOff value. */ + assert( p==0 || p==&((u8 *)pFd->pMapRegion)[iOff] ); + if( p ){ pFd->nFetchOut--; }else{ -- cgit v1.2.3 From 893c0ffc290bb59bcd8f505bec45087211579525 Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 25 Mar 2013 19:05:07 +0000 Subject: Add a test that simulates an error in mmap(). FossilOrigin-Name: 6ec7367d8e98425f00eeb8215ca8964313c1d0b7 --- src/os_unix.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src/os_unix.c') diff --git a/src/os_unix.c b/src/os_unix.c index 00c0088f8..8175bd5c9 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -444,6 +444,9 @@ static struct unix_syscall { { "fchown", (sqlite3_syscall_ptr)posixFchown, 0 }, #define osFchown ((int(*)(int,uid_t,gid_t))aSyscall[20].pCurrent) + { "mmap", (sqlite3_syscall_ptr)mmap, 0 }, +#define osMmap ((void*(*)(void*,size_t,int,int,int,off_t))aSyscall[21].pCurrent) + }; /* End of the overrideable system calls */ /* @@ -4547,7 +4550,7 @@ static int unixMapfile(unixFile *pFd, i64 nByte){ void *pNew; int flags = PROT_READ; if( (pFd->ctrlFlags & UNIXFILE_RDONLY)==0 ) flags |= PROT_WRITE; - pNew = mmap(0, nMap, flags, MAP_SHARED, pFd->h, 0); + pNew = osMmap(0, nMap, flags, MAP_SHARED, pFd->h, 0); if( pNew==MAP_FAILED ){ return SQLITE_IOERR_MMAP; } @@ -7186,7 +7189,7 @@ int sqlite3_os_init(void){ /* Double-check that the aSyscall[] array has been constructed ** correctly. See ticket [bb3a86e890c8e96ab] */ - assert( ArraySize(aSyscall)==21 ); + assert( ArraySize(aSyscall)==22 ); /* Register all VFSes defined in the aVfs[] array */ for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){ -- cgit v1.2.3 From b7e3a326fe84ff10e9b34a9c11c32e441ac0fc5b Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 25 Mar 2013 20:30:13 +0000 Subject: Use mremap() on Linux. FossilOrigin-Name: 431aecc8600c29c203546e48d256510510238887 --- src/os_unix.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) (limited to 'src/os_unix.c') diff --git a/src/os_unix.c b/src/os_unix.c index 9d2e2c130..33be79342 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -4544,21 +4544,28 @@ static int unixMapfile(unixFile *pFd, i64 nByte){ } if( nMap!=pFd->mmapSize ){ - unixUnmapfile(pFd); + void *pNew = 0; - if( nMap>0 ){ - void *pNew; - int flags = PROT_READ; - if( (pFd->ctrlFlags & UNIXFILE_RDONLY)==0 ) flags |= PROT_WRITE; - pNew = osMmap(0, nMap, flags, MAP_SHARED, pFd->h, 0); - if( pNew==MAP_FAILED ){ - return SQLITE_IOERR_MMAP; +#if defined(__linux__) && defined(_GNU_SOURCE) + if( pFd->pMapRegion && nMap>0 ){ + pNew = mremap(pFd->pMapRegion, pFd->mmapOrigsize, nMap, MREMAP_MAYMOVE); + }else +#endif + { + unixUnmapfile(pFd); + if( nMap>0 ){ + int flags = PROT_READ; + if( (pFd->ctrlFlags & UNIXFILE_RDONLY)==0 ) flags |= PROT_WRITE; + pNew = osMmap(0, nMap, flags, MAP_SHARED, pFd->h, 0); } + } - pFd->pMapRegion = pNew; - pFd->mmapSize = nMap; - pFd->mmapOrigsize = nMap; + if( pNew==MAP_FAILED ){ + return SQLITE_IOERR_MMAP; } + pFd->pMapRegion = pNew; + pFd->mmapSize = nMap; + pFd->mmapOrigsize = nMap; } return SQLITE_OK; -- cgit v1.2.3 From d1ab8065c1039db43eb413702cafb3baa500d69a Mon Sep 17 00:00:00 2001 From: drh Date: Mon, 25 Mar 2013 20:50:25 +0000 Subject: Add munmap and mremap to the set of os interfaces that can be overloaded in os_unix.c. FossilOrigin-Name: 8776047bd776bbf266eb9c3b56683badb84ae73e --- src/os_unix.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) (limited to 'src/os_unix.c') diff --git a/src/os_unix.c b/src/os_unix.c index 33be79342..625f41340 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -447,6 +447,16 @@ static struct unix_syscall { { "mmap", (sqlite3_syscall_ptr)mmap, 0 }, #define osMmap ((void*(*)(void*,size_t,int,int,int,off_t))aSyscall[21].pCurrent) + { "munmap", (sqlite3_syscall_ptr)munmap, 0 }, +#define osMunmap ((void*(*)(void*,size_t))aSyscall[22].pCurrent) + +#if defined(__linux__) && defined(_GNU_SOURCE) + { "mremap", (sqlite3_syscall_ptr)mremap, 0 }, +#else + { "mremap", (sqlite3_syscall_ptr)0, 0 }, +#endif +#define osMremap ((void*(*)(void*,size_t,size_t,int,...))aSyscall[23].pCurrent) + }; /* End of the overrideable system calls */ /* @@ -4005,7 +4015,7 @@ static void unixShmPurge(unixFile *pFd){ sqlite3_mutex_free(p->mutex); for(i=0; inRegion; i++){ if( p->h>=0 ){ - munmap(p->apRegion[i], p->szRegion); + osMunmap(p->apRegion[i], p->szRegion); }else{ sqlite3_free(p->apRegion[i]); } @@ -4278,7 +4288,7 @@ static int unixShmMap( while(pShmNode->nRegion<=iRegion){ void *pMem; if( pShmNode->h>=0 ){ - pMem = mmap(0, szRegion, + pMem = osMmap(0, szRegion, pShmNode->isReadonly ? PROT_READ : PROT_READ|PROT_WRITE, MAP_SHARED, pShmNode->h, szRegion*(i64)pShmNode->nRegion ); @@ -4501,7 +4511,7 @@ static int unixShmUnmap( static void unixUnmapfile(unixFile *pFd){ assert( pFd->nFetchOut==0 ); if( pFd->pMapRegion ){ - munmap(pFd->pMapRegion, pFd->mmapOrigsize); + osMunmap(pFd->pMapRegion, pFd->mmapOrigsize); pFd->pMapRegion = 0; pFd->mmapSize = 0; pFd->mmapOrigsize = 0; @@ -4548,7 +4558,7 @@ static int unixMapfile(unixFile *pFd, i64 nByte){ #if defined(__linux__) && defined(_GNU_SOURCE) if( pFd->pMapRegion && nMap>0 ){ - pNew = mremap(pFd->pMapRegion, pFd->mmapOrigsize, nMap, MREMAP_MAYMOVE); + pNew = osMremap(pFd->pMapRegion, pFd->mmapOrigsize, nMap, MREMAP_MAYMOVE); }else #endif { @@ -7196,7 +7206,7 @@ int sqlite3_os_init(void){ /* Double-check that the aSyscall[] array has been constructed ** correctly. See ticket [bb3a86e890c8e96ab] */ - assert( ArraySize(aSyscall)==22 ); + assert( ArraySize(aSyscall)==24 ); /* Register all VFSes defined in the aVfs[] array */ for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){ -- cgit v1.2.3 From 0d0614bdc6e59c1cb52bc79fdf8dafbbc78f57f9 Mon Sep 17 00:00:00 2001 From: drh Date: Mon, 25 Mar 2013 23:09:28 +0000 Subject: Memory-mapped I/O is now on by default. The "PRAGMA mmap_limit(N)" can be used to issue a hint to the VFS to limit mmap space to N bytes. The VFS is free to ignore that hint if desired. However, if "PRAGMA mmap_limit(0)" is used, xFetch is never called. FossilOrigin-Name: 1b37c4effdd03aa2ea938a71b4f22ed27391689b --- src/os_unix.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'src/os_unix.c') diff --git a/src/os_unix.c b/src/os_unix.c index 625f41340..8a8516bcd 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -225,6 +225,11 @@ struct unixFile { const char *zPath; /* Name of the file */ unixShm *pShm; /* Shared memory segment information */ int szChunk; /* Configured by FCNTL_CHUNK_SIZE */ + int nFetchOut; /* Number of outstanding xFetch refs */ + sqlite3_int64 mmapSize; /* Usable size of mapping at pMapRegion */ + sqlite3_int64 mmapOrigsize; /* Actual size of mapping at pMapRegion */ + sqlite3_int64 mmapLimit; /* Configured FCNTL_MMAP_LIMIT value */ + void *pMapRegion; /* Memory mapped region */ #ifdef __QNXNTO__ int sectorSize; /* Device sector size */ int deviceCharacteristics; /* Precomputed device characteristics */ @@ -251,11 +256,6 @@ struct unixFile { unsigned char inNormalWrite; /* True if in a normal write operation */ #endif - sqlite3_int64 mmapSize; /* Usable size of mapping at pMapRegion */ - sqlite3_int64 mmapOrigsize; /* Actual size of mapping at pMapRegion */ - sqlite3_int64 mmapLimit; /* Configured FCNTL_MMAP_SIZE value */ - void *pMapRegion; /* Memory mapped region */ - int nFetchOut; /* Number of outstanding xFetch refs */ #ifdef SQLITE_TEST /* In test mode, increase the size of this structure a bit so that @@ -3699,7 +3699,7 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){ } return SQLITE_OK; } - case SQLITE_FCNTL_MMAP_SIZE: { + case SQLITE_FCNTL_MMAP_LIMIT: { pFile->mmapLimit = *(i64*)pArg; return SQLITE_OK; } @@ -4528,7 +4528,7 @@ static void unixUnmapfile(unixFile *pFd){ ** the mapping to create. Otherwise, if nByte is less than zero, then the ** requested size is the size of the file on disk. The actual size of the ** created mapping is either the requested size or the value configured -** using SQLITE_FCNTL_MMAP_SIZE, whichever is smaller. +** using SQLITE_FCNTL_MMAP_LIMIT, whichever is smaller. ** ** SQLITE_OK is returned if no error occurs (even if the mapping is not ** recreated as a result of outstanding references) or an SQLite error @@ -4969,6 +4969,7 @@ static int fillInUnixFile( pNew->pVfs = pVfs; pNew->zPath = zFilename; pNew->ctrlFlags = (u8)ctrlFlags; + pNew->mmapLimit = SQLITE_DEFAULT_MMAP_LIMIT; if( sqlite3_uri_boolean(((ctrlFlags & UNIXFILE_URI) ? zFilename : 0), "psow", SQLITE_POWERSAFE_OVERWRITE) ){ pNew->ctrlFlags |= UNIXFILE_PSOW; -- cgit v1.2.3 From 6c5696381e5720eaa9bd6c780bd75f5669f04c96 Mon Sep 17 00:00:00 2001 From: drh Date: Tue, 26 Mar 2013 18:48:11 +0000 Subject: Fix a comment in os_unix.c. No code changes. FossilOrigin-Name: 72813b8ec924b91583c679668f7c4561dff82a02 --- src/os_unix.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/os_unix.c') diff --git a/src/os_unix.c b/src/os_unix.c index 8a8516bcd..f91f4c4e7 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -3107,8 +3107,8 @@ static int unixRead( ); #endif - /* Deal with as much of this write request as possible by transfering - ** data to the memory mapping using memcpy(). */ + /* Deal with as much of this read request as possible by transfering + ** data from the memory mapping using memcpy(). */ if( offsetmmapSize ){ if( offset+amt <= pFile->mmapSize ){ memcpy(pBuf, &((u8 *)(pFile->pMapRegion))[offset], amt); -- cgit v1.2.3 From e6ecd6630dd7fadec974ffd94ea678212e959717 Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 1 Apr 2013 17:56:59 +0000 Subject: Attempt to emulate mremap() on non-Linux systems by allocating a second mapping immediately following the first in virtual memory. FossilOrigin-Name: 4d67433db8fb4754ae6b192945e479f3d7bad579 --- src/os_unix.c | 137 ++++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 114 insertions(+), 23 deletions(-) (limited to 'src/os_unix.c') diff --git a/src/os_unix.c b/src/os_unix.c index f91f4c4e7..e92c7cc4a 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -313,6 +313,17 @@ struct unixFile { #define threadid 0 #endif +/* +** HAVE_MREMAP defaults to true on Linux and false everywhere else. +*/ +#if !defined(HAVE_MREMAP) +# if defined(__linux__) && defined(_GNU_SOURCE) +# define HAVE_MREMAP 1 +# else +# define HAVE_MREMAP 0 +# endif +#endif + /* ** Different Unix systems declare open() in different ways. Same use ** open(const char*,int,mode_t). Others use open(const char*,int,...). @@ -450,7 +461,7 @@ static struct unix_syscall { { "munmap", (sqlite3_syscall_ptr)munmap, 0 }, #define osMunmap ((void*(*)(void*,size_t))aSyscall[22].pCurrent) -#if defined(__linux__) && defined(_GNU_SOURCE) +#if HAVE_MREMAP { "mremap", (sqlite3_syscall_ptr)mremap, 0 }, #else { "mremap", (sqlite3_syscall_ptr)0, 0 }, @@ -1124,7 +1135,6 @@ static int unixLogErrorAtLine( zErr = strerror(iErrno); #endif - assert( errcode!=SQLITE_OK ); if( zPath==0 ) zPath = ""; sqlite3_log(errcode, "os_unix.c:%d: (%d) %s(%s) - %s", @@ -3619,7 +3629,7 @@ static int fcntlSizeHint(unixFile *pFile, i64 nByte){ } } - if( pFile->mmapLimit>0 ){ + if( pFile->mmapLimit>0 && nByte>pFile->mmapSize ){ int rc; if( pFile->szChunk<=0 ){ if( robust_ftruncate(pFile->h, nByte) ){ @@ -4518,6 +4528,104 @@ static void unixUnmapfile(unixFile *pFd){ } } +/* +** Return the system page size. +*/ +static int unixGetPagesize(void){ +#if HAVE_MREMAP + return 512; +#elif _BSD_SOURCE + return getpagesize(); +#else + return (int)sysconf(_SC_PAGESIZE); +#endif +} + +/* +** Attempt to set the size of the memory mapping maintained by file +** descriptor pFd to nNew bytes. Any existing mapping is discarded. +** +** If successful, this function sets the following variables: +** +** unixFile.pMapRegion +** unixFile.mmapSize +** unixFile.mmapOrigsize +** +** If unsuccessful, an error message is logged via sqlite3_log() and +** the three variables above are zeroed. In this case SQLite should +** continue accessing the database using the xRead() and xWrite() +** methods. +*/ +static void unixRemapfile( + unixFile *pFd, /* File descriptor object */ + i64 nNew /* Required mapping size */ +){ + int h = pFd->h; /* File descriptor open on db file */ + u8 *pOrig = (u8 *)pFd->pMapRegion; /* Pointer to current file mapping */ + i64 nOrig = pFd->mmapOrigsize; /* Size of pOrig region in bytes */ + u8 *pNew = 0; /* Location of new mapping */ + int flags = PROT_READ; /* Flags to pass to mmap() */ + + assert( pFd->nFetchOut==0 ); + assert( nNew>pFd->mmapSize ); + assert( nNew<=pFd->mmapLimit ); + assert( nNew>0 ); + assert( pFd->mmapOrigsize>=pFd->mmapSize ); + + if( (pFd->ctrlFlags & UNIXFILE_RDONLY)==0 ) flags |= PROT_WRITE; + + if( pOrig ){ + const int szSyspage = unixGetPagesize(); + i64 nReuse = (pFd->mmapSize & ~(szSyspage-1)); + u8 *pReq = &pOrig[nReuse]; + + /* Unmap any pages of the existing mapping that cannot be reused. */ + if( nReuse!=nOrig ){ + osMunmap(pReq, nOrig-nReuse); + } + +#if HAVE_MREMAP + pNew = osMremap(pOrig, nReuse, nNew, MREMAP_MAYMOVE); +#else + pNew = osMmap(pReq, nNew-nReuse, flags, MAP_SHARED, h, nReuse); + if( pNew!=MAP_FAILED ){ + if( pNew!=pReq ){ + osMunmap(pNew, nNew - nReuse); + pNew = MAP_FAILED; + }else{ + pNew = pOrig; + } + } +#endif + + /* The attempt to extend the existing mapping failed. Free the existing + ** mapping and set pNew to NULL so that the code below will create a + ** new mapping from scratch. */ + if( pNew==MAP_FAILED ){ + pNew = 0; + osMunmap(pOrig, nReuse); + } + } + + /* If pNew is still NULL, try to create an entirely new mapping. */ + if( pNew==0 ){ + pNew = osMmap(0, nNew, flags, MAP_SHARED, h, 0); + if( pNew==MAP_FAILED ){ + pNew = 0; + nNew = 0; + unixLogError(SQLITE_OK, "mmap", pFd->zPath); + + /* If the mmap() above failed, assume that all subsequent mmap() calls + ** will probably fail too. Fall back to using xRead/xWrite exclusively + ** in this case. */ + pFd->mmapLimit = 0; + } + } + + pFd->pMapRegion = (void *)pNew; + pFd->mmapSize = pFd->mmapOrigsize = nNew; +} + /* ** Memory map or remap the file opened by file-descriptor pFd (if the file ** is already mapped, the existing mapping is replaced by the new). Or, if @@ -4554,28 +4662,11 @@ static int unixMapfile(unixFile *pFd, i64 nByte){ } if( nMap!=pFd->mmapSize ){ - void *pNew = 0; - -#if defined(__linux__) && defined(_GNU_SOURCE) - if( pFd->pMapRegion && nMap>0 ){ - pNew = osMremap(pFd->pMapRegion, pFd->mmapOrigsize, nMap, MREMAP_MAYMOVE); - }else -#endif - { + if( nMap>0 ){ + unixRemapfile(pFd, nMap); + }else{ unixUnmapfile(pFd); - if( nMap>0 ){ - int flags = PROT_READ; - if( (pFd->ctrlFlags & UNIXFILE_RDONLY)==0 ) flags |= PROT_WRITE; - pNew = osMmap(0, nMap, flags, MAP_SHARED, pFd->h, 0); - } - } - - if( pNew==MAP_FAILED ){ - return SQLITE_IOERR_MMAP; } - pFd->pMapRegion = pNew; - pFd->mmapSize = nMap; - pFd->mmapOrigsize = nMap; } return SQLITE_OK; -- cgit v1.2.3 From a1f42c7c32b7326568305f64cf5c592910928a3e Mon Sep 17 00:00:00 2001 From: drh Date: Mon, 1 Apr 2013 22:38:06 +0000 Subject: Add the SQLITE_CONFIG_MMAP_LIMIT configuration option for overriding the SQLITE_DEFAULT_MMAP_LIMIT compile-time setting. Enhance "PRAGMA mmap_limit" so that without a specific database name, it sets the limit on all database files and changes the default for any future databases that might be added using ATTACH. FossilOrigin-Name: 78141d0a16dd1d56b575fccd149de7fa789cb06c --- src/os_unix.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/os_unix.c') diff --git a/src/os_unix.c b/src/os_unix.c index e92c7cc4a..c10841abf 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -3711,6 +3711,7 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){ } case SQLITE_FCNTL_MMAP_LIMIT: { pFile->mmapLimit = *(i64*)pArg; +printf("MMAP-LIMIT(%s) -> %lld\n", pFile->zPath, pFile->mmapLimit); return SQLITE_OK; } #ifdef SQLITE_DEBUG -- cgit v1.2.3 From 3861f546e3f3752cdce48fb75e37da7394aba882 Mon Sep 17 00:00:00 2001 From: drh Date: Mon, 1 Apr 2013 22:42:48 +0000 Subject: Remove a debugging printf() accidently left in the previous check-in. FossilOrigin-Name: 8198cdd8ac5dcc1c677fffa869ac965186b96abf --- src/os_unix.c | 1 - 1 file changed, 1 deletion(-) (limited to 'src/os_unix.c') diff --git a/src/os_unix.c b/src/os_unix.c index c10841abf..e92c7cc4a 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -3711,7 +3711,6 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){ } case SQLITE_FCNTL_MMAP_LIMIT: { pFile->mmapLimit = *(i64*)pArg; -printf("MMAP-LIMIT(%s) -> %lld\n", pFile->zPath, pFile->mmapLimit); return SQLITE_OK; } #ifdef SQLITE_DEBUG -- cgit v1.2.3 From 4ff7bc45c6c6a7156e433ff50395dfe5e2885c38 Mon Sep 17 00:00:00 2001 From: dan Date: Tue, 2 Apr 2013 12:04:09 +0000 Subject: Add test cases for errors in mmap() or mremap() is os_unix.c. FossilOrigin-Name: 3098a3c1e7305033904a496ef534cb312a876fab --- src/os_unix.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) (limited to 'src/os_unix.c') diff --git a/src/os_unix.c b/src/os_unix.c index e92c7cc4a..07c919c98 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -4560,6 +4560,7 @@ static void unixRemapfile( unixFile *pFd, /* File descriptor object */ i64 nNew /* Required mapping size */ ){ + const char *zErr = "mmap"; int h = pFd->h; /* File descriptor open on db file */ u8 *pOrig = (u8 *)pFd->pMapRegion; /* Pointer to current file mapping */ i64 nOrig = pFd->mmapOrigsize; /* Size of pOrig region in bytes */ @@ -4571,6 +4572,7 @@ static void unixRemapfile( assert( nNew<=pFd->mmapLimit ); assert( nNew>0 ); assert( pFd->mmapOrigsize>=pFd->mmapSize ); + assert( MAP_FAILED!=0 ); if( (pFd->ctrlFlags & UNIXFILE_RDONLY)==0 ) flags |= PROT_WRITE; @@ -4586,12 +4588,13 @@ static void unixRemapfile( #if HAVE_MREMAP pNew = osMremap(pOrig, nReuse, nNew, MREMAP_MAYMOVE); + zErr = "mremap"; #else pNew = osMmap(pReq, nNew-nReuse, flags, MAP_SHARED, h, nReuse); if( pNew!=MAP_FAILED ){ if( pNew!=pReq ){ osMunmap(pNew, nNew - nReuse); - pNew = MAP_FAILED; + pNew = 0; }else{ pNew = pOrig; } @@ -4602,7 +4605,6 @@ static void unixRemapfile( ** mapping and set pNew to NULL so that the code below will create a ** new mapping from scratch. */ if( pNew==MAP_FAILED ){ - pNew = 0; osMunmap(pOrig, nReuse); } } @@ -4610,18 +4612,18 @@ static void unixRemapfile( /* If pNew is still NULL, try to create an entirely new mapping. */ if( pNew==0 ){ pNew = osMmap(0, nNew, flags, MAP_SHARED, h, 0); - if( pNew==MAP_FAILED ){ - pNew = 0; - nNew = 0; - unixLogError(SQLITE_OK, "mmap", pFd->zPath); - - /* If the mmap() above failed, assume that all subsequent mmap() calls - ** will probably fail too. Fall back to using xRead/xWrite exclusively - ** in this case. */ - pFd->mmapLimit = 0; - } } + if( pNew==MAP_FAILED ){ + pNew = 0; + nNew = 0; + unixLogError(SQLITE_OK, zErr, pFd->zPath); + + /* If the mmap() above failed, assume that all subsequent mmap() calls + ** will probably fail too. Fall back to using xRead/xWrite exclusively + ** in this case. */ + pFd->mmapLimit = 0; + } pFd->pMapRegion = (void *)pNew; pFd->mmapSize = pFd->mmapOrigsize = nNew; } -- cgit v1.2.3 From 48ccef80595731d2c2b6d759455837db3113f647 Mon Sep 17 00:00:00 2001 From: dan Date: Tue, 2 Apr 2013 20:55:01 +0000 Subject: Fix a resource leak in os_unix.c. FossilOrigin-Name: b29cda03fe4e8d8f5b5acbbea2d69f284a2bdf23 --- src/os_unix.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'src/os_unix.c') diff --git a/src/os_unix.c b/src/os_unix.c index 07c919c98..581afdbc9 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -4601,10 +4601,8 @@ static void unixRemapfile( } #endif - /* The attempt to extend the existing mapping failed. Free the existing - ** mapping and set pNew to NULL so that the code below will create a - ** new mapping from scratch. */ - if( pNew==MAP_FAILED ){ + /* The attempt to extend the existing mapping failed. Free it. */ + if( pNew==MAP_FAILED || pNew==0 ){ osMunmap(pOrig, nReuse); } } -- cgit v1.2.3 From 85830a70c2d07e345564211e13e7f5e6182f4135 Mon Sep 17 00:00:00 2001 From: drh Date: Wed, 3 Apr 2013 00:42:01 +0000 Subject: Fix the unix driver to check defined(_BSD_SOURCE) rather than just the plain _BSD_SOURCE macro. This fixes the build for OpenBSD. FossilOrigin-Name: 1dd42ef4144ee08fb4ee1676d934a56a0e34bac2 --- src/os_unix.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/os_unix.c') diff --git a/src/os_unix.c b/src/os_unix.c index 581afdbc9..6ca99fa53 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -4534,7 +4534,7 @@ static void unixUnmapfile(unixFile *pFd){ static int unixGetPagesize(void){ #if HAVE_MREMAP return 512; -#elif _BSD_SOURCE +#elif defined(_BSD_SOURCE) return getpagesize(); #else return (int)sysconf(_SC_PAGESIZE); -- cgit v1.2.3 From 2b8246e3e23cb98b6d7339de019d446aa3bc3bb8 Mon Sep 17 00:00:00 2001 From: drh Date: Wed, 3 Apr 2013 10:50:02 +0000 Subject: Initialize the mmap_limit of temporary files to the configured mmap_limit. FossilOrigin-Name: 24bab7596bb7385981a5d331df5eeb05353547f7 --- src/os_unix.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/os_unix.c') diff --git a/src/os_unix.c b/src/os_unix.c index 6ca99fa53..39a31cb18 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -5060,7 +5060,7 @@ static int fillInUnixFile( pNew->pVfs = pVfs; pNew->zPath = zFilename; pNew->ctrlFlags = (u8)ctrlFlags; - pNew->mmapLimit = SQLITE_DEFAULT_MMAP_LIMIT; + pNew->mmapLimit = sqlite3GlobalConfig.mxMmap; if( sqlite3_uri_boolean(((ctrlFlags & UNIXFILE_URI) ? zFilename : 0), "psow", SQLITE_POWERSAFE_OVERWRITE) ){ pNew->ctrlFlags |= UNIXFILE_PSOW; -- cgit v1.2.3 From 34f7490311f77d0da8c90260d083f19550fd6c61 Mon Sep 17 00:00:00 2001 From: drh Date: Wed, 3 Apr 2013 13:09:18 +0000 Subject: Change the mmap_limit pragma to report the new limit, or to report the existing limit if called with no arguments. Report the default mmap_limit as part of PRAGMA compile_options. Set the default mmmap_limit to 0 for all systems other than linux, mac, windows, and solaris. FossilOrigin-Name: 2d9f1327fe79e40435ce1e2594d7cd9a5aea0ef2 --- src/os_unix.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/os_unix.c') diff --git a/src/os_unix.c b/src/os_unix.c index 39a31cb18..75f923601 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -3710,7 +3710,9 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){ return SQLITE_OK; } case SQLITE_FCNTL_MMAP_LIMIT: { - pFile->mmapLimit = *(i64*)pArg; + i64 newLimit = *(i64*)pArg; + *(i64*)pArg = pFile->mmapLimit; + if( newLimit>=0 ) pFile->mmapLimit = newLimit; return SQLITE_OK; } #ifdef SQLITE_DEBUG -- cgit v1.2.3 From bcb8a868cec66d9edd20e99f7bdb5d2959e2a60d Mon Sep 17 00:00:00 2001 From: dan Date: Mon, 8 Apr 2013 15:30:41 +0000 Subject: Handle the case in os_unix.c where SQLITE_FCNTL_MMAP_LIMIT requests that the mmap limit be set to a value smaller than the current mapping. FossilOrigin-Name: 360473493ec1a7094a2b1c5436f3b70914a6dfdd --- src/os_unix.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/os_unix.c') diff --git a/src/os_unix.c b/src/os_unix.c index 75f923601..1d4c8ad34 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -3712,7 +3712,10 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){ case SQLITE_FCNTL_MMAP_LIMIT: { i64 newLimit = *(i64*)pArg; *(i64*)pArg = pFile->mmapLimit; - if( newLimit>=0 ) pFile->mmapLimit = newLimit; + if( newLimit>=0 ){ + pFile->mmapLimit = newLimit; + if( newLimitmmapSize ) pFile->mmapSize = newLimit; + } return SQLITE_OK; } #ifdef SQLITE_DEBUG -- cgit v1.2.3 From 6e0b6d52da6a6245c97a40997a95f8cb14fed502 Mon Sep 17 00:00:00 2001 From: drh Date: Tue, 9 Apr 2013 16:19:20 +0000 Subject: Add extra #ifndef statements in os_unix.c and os_win.c to make sure the memory mapped I/O really is disabled when SQLITE_DISABLE_MMAP is set. FossilOrigin-Name: c1e2523c9051782569291fff998140f7e0b70b6d --- src/os_unix.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'src/os_unix.c') diff --git a/src/os_unix.c b/src/os_unix.c index 1d4c8ad34..a65f894eb 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -3117,6 +3117,7 @@ static int unixRead( ); #endif +#if !defined(SQLITE_DISABLE_MMAP) /* Deal with as much of this read request as possible by transfering ** data from the memory mapping using memcpy(). */ if( offsetmmapSize ){ @@ -3131,6 +3132,7 @@ static int unixRead( offset += nCopy; } } +#endif got = seekAndRead(pFile, offset, pBuf, amt); if( got==amt ){ @@ -3236,6 +3238,7 @@ static int unixWrite( } #endif +#if !defined(SQLITE_DISABLE_MMAP) /* Deal with as much of this write request as possible by transfering ** data from the memory mapping using memcpy(). */ if( offsetmmapSize ){ @@ -3250,6 +3253,7 @@ static int unixWrite( offset += nCopy; } } +#endif while( amt>0 && (wrote = seekAndWrite(pFile, offset, pBuf, amt))>0 ){ amt -= wrote; @@ -4525,12 +4529,14 @@ static int unixShmUnmap( */ static void unixUnmapfile(unixFile *pFd){ assert( pFd->nFetchOut==0 ); +#ifndef SQLITE_DISABLE_MMAP if( pFd->pMapRegion ){ osMunmap(pFd->pMapRegion, pFd->mmapOrigsize); pFd->pMapRegion = 0; pFd->mmapSize = 0; pFd->mmapOrigsize = 0; } +#endif } /* @@ -4546,6 +4552,7 @@ static int unixGetPagesize(void){ #endif } +#ifndef SQLITE_DISABLE_MMAP /* ** Attempt to set the size of the memory mapping maintained by file ** descriptor pFd to nNew bytes. Any existing mapping is discarded. @@ -4630,6 +4637,7 @@ static void unixRemapfile( pFd->pMapRegion = (void *)pNew; pFd->mmapSize = pFd->mmapOrigsize = nNew; } +#endif /* ** Memory map or remap the file opened by file-descriptor pFd (if the file @@ -4651,6 +4659,7 @@ static int unixMapfile(unixFile *pFd, i64 nByte){ i64 nMap = nByte; int rc; +#ifndef SQLITE_DISABLE_MMAP assert( nMap>=0 || pFd->nFetchOut==0 ); if( pFd->nFetchOut>0 ) return SQLITE_OK; @@ -4673,6 +4682,7 @@ static int unixMapfile(unixFile *pFd, i64 nByte){ unixUnmapfile(pFd); } } +#endif return SQLITE_OK; } @@ -4693,6 +4703,7 @@ static int unixFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){ unixFile *pFd = (unixFile *)fd; /* The underlying database file */ *pp = 0; +#ifndef SQLITE_DISABLE_MMAP if( pFd->mmapLimit>0 ){ if( pFd->pMapRegion==0 ){ int rc = unixMapfile(pFd, -1); @@ -4703,6 +4714,7 @@ static int unixFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){ pFd->nFetchOut++; } } +#endif return SQLITE_OK; } -- cgit v1.2.3 From fbc7e8845d6c3a0b140306d46c3454ca6ac45288 Mon Sep 17 00:00:00 2001 From: drh Date: Thu, 11 Apr 2013 01:16:15 +0000 Subject: Have the UNIX VFS issue warnings via sqlite3_log() if a database file is renamed or unlinked or linked to more than one name while the file is open. FossilOrigin-Name: e238dcf9189c029fbdcf89339e21d9cdd8fbf2c5 --- src/os_unix.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) (limited to 'src/os_unix.c') diff --git a/src/os_unix.c b/src/os_unix.c index a65f894eb..778575f7b 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -280,6 +280,7 @@ struct unixFile { #define UNIXFILE_DELETE 0x20 /* Delete on close */ #define UNIXFILE_URI 0x40 /* Filename might have query parameters */ #define UNIXFILE_NOLOCK 0x80 /* Do no file locking */ +#define UNIXFILE_WARNED 0x0100 /* verifyDbFile() warnings have been issued */ /* ** Include code that is common to all os_*.c files @@ -799,7 +800,6 @@ static int sqliteErrorFromPosixError(int posixError, int sqliteIOErr) { } - /****************************************************************************** ****************** Begin Unique File ID Utility Used By VxWorks *************** ** @@ -1300,6 +1300,50 @@ static int findInodeInfo( } +/* +** Check a unixFile that is a database. Verify the following: +** +** (1) There is exactly one hard link on the file +** (2) The file is not a symbolic link +** (3) The file has not been renamed or unlinked +** +** Issue sqlite3_log(SQLITE_WARNING,...) messages if anything is not right. +*/ +static void verifyDbFile(unixFile *pFile){ + struct stat buf; + int rc; + if( pFile->ctrlFlags & UNIXFILE_WARNED ){ + /* One or more of the following warnings have already been issued. Do not + ** repeat them so as not to clutter the error log */ + return; + } + rc = osFstat(pFile->h, &buf); + if( rc!=0 ){ + sqlite3_log(SQLITE_WARNING, "cannot fstat db file %s", pFile->zPath); + pFile->ctrlFlags |= UNIXFILE_WARNED; + return; + } + if( buf.st_nlink==0 && (pFile->ctrlFlags & UNIXFILE_DELETE)==0 ){ + sqlite3_log(SQLITE_WARNING, "file unlinked while open: %s", pFile->zPath); + pFile->ctrlFlags |= UNIXFILE_WARNED; + return; + } + if( buf.st_nlink>1 ){ + sqlite3_log(SQLITE_WARNING, "multiple links to file: %s", pFile->zPath); + pFile->ctrlFlags |= UNIXFILE_WARNED; + return; + } + if( pFile->pInode!=0 + && ((rc = osStat(pFile->zPath, &buf))!=0 + || buf.st_ino!=pFile->pInode->fileId.ino) + ){ + sqlite3_log(SQLITE_WARNING, "file renamed while open: %s", pFile->zPath); + pFile->ctrlFlags |= UNIXFILE_WARNED; + return; + } +} + + /* ** This routine checks if there is a RESERVED lock held on the specified ** file by this or any other process. If such a lock is held, set *pResOut @@ -1876,6 +1920,7 @@ static int closeUnixFile(sqlite3_file *id){ static int unixClose(sqlite3_file *id){ int rc = SQLITE_OK; unixFile *pFile = (unixFile *)id; + verifyDbFile(pFile); unixUnlock(id, NO_LOCK); unixEnterMutex(); @@ -4539,6 +4584,7 @@ static void unixUnmapfile(unixFile *pFd){ #endif } +#ifndef SQLITE_DISABLE_MMAP /* ** Return the system page size. */ @@ -4551,6 +4597,7 @@ static int unixGetPagesize(void){ return (int)sysconf(_SC_PAGESIZE); #endif } +#endif /* SQLITE_DISABLE_MMAP */ #ifndef SQLITE_DISABLE_MMAP /* @@ -4656,10 +4703,10 @@ static void unixRemapfile( ** code otherwise. */ static int unixMapfile(unixFile *pFd, i64 nByte){ +#ifndef SQLITE_DISABLE_MMAP i64 nMap = nByte; int rc; -#ifndef SQLITE_DISABLE_MMAP assert( nMap>=0 || pFd->nFetchOut==0 ); if( pFd->nFetchOut>0 ) return SQLITE_OK; @@ -4700,7 +4747,9 @@ static int unixMapfile(unixFile *pFd, i64 nByte){ ** release the reference by calling unixUnfetch(). */ static int unixFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){ +#ifndef SQLITE_DISABLE_MMAP unixFile *pFd = (unixFile *)fd; /* The underlying database file */ +#endif *pp = 0; #ifndef SQLITE_DISABLE_MMAP @@ -5222,6 +5271,7 @@ static int fillInUnixFile( }else{ pNew->pMethod = pLockingStyle; OpenCounter(+1); + verifyDbFile(pNew); } return rc; } -- cgit v1.2.3 From 9b4c59fa1b591e4474ca458a4af239dd9c1d540d Mon Sep 17 00:00:00 2001 From: drh Date: Mon, 15 Apr 2013 17:03:42 +0000 Subject: Refactoring the mmap interface. The controlling pragma is now "mmap_size" instead of "mmap_limit". Also change SQLITE_CONFIG_MMAP_LIMIT and SQLITE_FCNTL_MMAP_LIMIT to SQLITE_CONFIG_MMAP_SIZE and SQLITE_FCNTL_MMAP_SIZE, respecctively. The default mmap_size is now always 0, meaning that memory mapped I/O is off by default. There is a new compile-time option SQLITE_MAX_MMAP_SIZE that determines a hard upper bound on the mmap_size. Setting SQLITE_MAX_MMAP_SIZE to zero disables the memory-mapped I/O logic and causes it to be omitted from the build. An extra argument is added to SQLITE_CONFIG_MMAP_SIZE that can optionally lower the SQLITE_MAX_MMAP_SIZE at start-time. The SQLITE_MAX_MMAP_SIZE is zero for platforms where we know that it does not work, meaning that it cannot be turned on by mistake on those platforms. FossilOrigin-Name: ea1404a10abd7f68e1f8e0708c8a3199d1f79665 --- src/os_unix.c | 57 ++++++++++++++++++++++++++++++--------------------------- 1 file changed, 30 insertions(+), 27 deletions(-) (limited to 'src/os_unix.c') diff --git a/src/os_unix.c b/src/os_unix.c index 778575f7b..8dfbe6181 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -227,8 +227,8 @@ struct unixFile { int szChunk; /* Configured by FCNTL_CHUNK_SIZE */ int nFetchOut; /* Number of outstanding xFetch refs */ sqlite3_int64 mmapSize; /* Usable size of mapping at pMapRegion */ - sqlite3_int64 mmapOrigsize; /* Actual size of mapping at pMapRegion */ - sqlite3_int64 mmapLimit; /* Configured FCNTL_MMAP_LIMIT value */ + sqlite3_int64 mmapSizeActual; /* Actual size of mapping at pMapRegion */ + sqlite3_int64 mmapSizeMax; /* Configured FCNTL_MMAP_SIZE value */ void *pMapRegion; /* Memory mapped region */ #ifdef __QNXNTO__ int sectorSize; /* Device sector size */ @@ -3162,7 +3162,7 @@ static int unixRead( ); #endif -#if !defined(SQLITE_DISABLE_MMAP) +#if SQLITE_MAX_MMAP_SIZE>0 /* Deal with as much of this read request as possible by transfering ** data from the memory mapping using memcpy(). */ if( offsetmmapSize ){ @@ -3283,7 +3283,7 @@ static int unixWrite( } #endif -#if !defined(SQLITE_DISABLE_MMAP) +#if SQLITE_MAX_MMAP_SIZE>0 /* Deal with as much of this write request as possible by transfering ** data from the memory mapping using memcpy(). */ if( offsetmmapSize ){ @@ -3678,7 +3678,7 @@ static int fcntlSizeHint(unixFile *pFile, i64 nByte){ } } - if( pFile->mmapLimit>0 && nByte>pFile->mmapSize ){ + if( pFile->mmapSizeMax>0 && nByte>pFile->mmapSize ){ int rc; if( pFile->szChunk<=0 ){ if( robust_ftruncate(pFile->h, nByte) ){ @@ -3758,11 +3758,14 @@ static int unixFileControl(sqlite3_file *id, int op, void *pArg){ } return SQLITE_OK; } - case SQLITE_FCNTL_MMAP_LIMIT: { + case SQLITE_FCNTL_MMAP_SIZE: { i64 newLimit = *(i64*)pArg; - *(i64*)pArg = pFile->mmapLimit; + if( newLimit>sqlite3GlobalConfig.mxMmap ){ + newLimit = sqlite3GlobalConfig.mxMmap; + } + *(i64*)pArg = pFile->mmapSizeMax; if( newLimit>=0 ){ - pFile->mmapLimit = newLimit; + pFile->mmapSizeMax = newLimit; if( newLimitmmapSize ) pFile->mmapSize = newLimit; } return SQLITE_OK; @@ -4574,17 +4577,17 @@ static int unixShmUnmap( */ static void unixUnmapfile(unixFile *pFd){ assert( pFd->nFetchOut==0 ); -#ifndef SQLITE_DISABLE_MMAP +#if SQLITE_MAX_MMAP_SIZE>0 if( pFd->pMapRegion ){ - osMunmap(pFd->pMapRegion, pFd->mmapOrigsize); + osMunmap(pFd->pMapRegion, pFd->mmapSizeActual); pFd->pMapRegion = 0; pFd->mmapSize = 0; - pFd->mmapOrigsize = 0; + pFd->mmapSizeActual = 0; } #endif } -#ifndef SQLITE_DISABLE_MMAP +#if SQLITE_MAX_MMAP_SIZE>0 /* ** Return the system page size. */ @@ -4597,9 +4600,9 @@ static int unixGetPagesize(void){ return (int)sysconf(_SC_PAGESIZE); #endif } -#endif /* SQLITE_DISABLE_MMAP */ +#endif /* SQLITE_MAX_MMAP_SIZE>0 */ -#ifndef SQLITE_DISABLE_MMAP +#if SQLITE_MAX_MMAP_SIZE>0 /* ** Attempt to set the size of the memory mapping maintained by file ** descriptor pFd to nNew bytes. Any existing mapping is discarded. @@ -4608,7 +4611,7 @@ static int unixGetPagesize(void){ ** ** unixFile.pMapRegion ** unixFile.mmapSize -** unixFile.mmapOrigsize +** unixFile.mmapSizeActual ** ** If unsuccessful, an error message is logged via sqlite3_log() and ** the three variables above are zeroed. In this case SQLite should @@ -4622,15 +4625,15 @@ static void unixRemapfile( const char *zErr = "mmap"; int h = pFd->h; /* File descriptor open on db file */ u8 *pOrig = (u8 *)pFd->pMapRegion; /* Pointer to current file mapping */ - i64 nOrig = pFd->mmapOrigsize; /* Size of pOrig region in bytes */ + i64 nOrig = pFd->mmapSizeActual; /* Size of pOrig region in bytes */ u8 *pNew = 0; /* Location of new mapping */ int flags = PROT_READ; /* Flags to pass to mmap() */ assert( pFd->nFetchOut==0 ); assert( nNew>pFd->mmapSize ); - assert( nNew<=pFd->mmapLimit ); + assert( nNew<=pFd->mmapSizeMax ); assert( nNew>0 ); - assert( pFd->mmapOrigsize>=pFd->mmapSize ); + assert( pFd->mmapSizeActual>=pFd->mmapSize ); assert( MAP_FAILED!=0 ); if( (pFd->ctrlFlags & UNIXFILE_RDONLY)==0 ) flags |= PROT_WRITE; @@ -4679,10 +4682,10 @@ static void unixRemapfile( /* If the mmap() above failed, assume that all subsequent mmap() calls ** will probably fail too. Fall back to using xRead/xWrite exclusively ** in this case. */ - pFd->mmapLimit = 0; + pFd->mmapSizeMax = 0; } pFd->pMapRegion = (void *)pNew; - pFd->mmapSize = pFd->mmapOrigsize = nNew; + pFd->mmapSize = pFd->mmapSizeActual = nNew; } #endif @@ -4703,7 +4706,7 @@ static void unixRemapfile( ** code otherwise. */ static int unixMapfile(unixFile *pFd, i64 nByte){ -#ifndef SQLITE_DISABLE_MMAP +#if SQLITE_MAX_MMAP_SIZE>0 i64 nMap = nByte; int rc; @@ -4718,8 +4721,8 @@ static int unixMapfile(unixFile *pFd, i64 nByte){ } nMap = statbuf.st_size; } - if( nMap>pFd->mmapLimit ){ - nMap = pFd->mmapLimit; + if( nMap>pFd->mmapSizeMax ){ + nMap = pFd->mmapSizeMax; } if( nMap!=pFd->mmapSize ){ @@ -4747,13 +4750,13 @@ static int unixMapfile(unixFile *pFd, i64 nByte){ ** release the reference by calling unixUnfetch(). */ static int unixFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){ -#ifndef SQLITE_DISABLE_MMAP +#if SQLITE_MAX_MMAP_SIZE>0 unixFile *pFd = (unixFile *)fd; /* The underlying database file */ #endif *pp = 0; -#ifndef SQLITE_DISABLE_MMAP - if( pFd->mmapLimit>0 ){ +#if SQLITE_MAX_MMAP_SIZE>0 + if( pFd->mmapSizeMax>0 ){ if( pFd->pMapRegion==0 ){ int rc = unixMapfile(pFd, -1); if( rc!=SQLITE_OK ) return rc; @@ -5126,7 +5129,7 @@ static int fillInUnixFile( pNew->pVfs = pVfs; pNew->zPath = zFilename; pNew->ctrlFlags = (u8)ctrlFlags; - pNew->mmapLimit = sqlite3GlobalConfig.mxMmap; + pNew->mmapSizeMax = sqlite3GlobalConfig.mxMmap; if( sqlite3_uri_boolean(((ctrlFlags & UNIXFILE_URI) ? zFilename : 0), "psow", SQLITE_POWERSAFE_OVERWRITE) ){ pNew->ctrlFlags |= UNIXFILE_PSOW; -- cgit v1.2.3 From da8caa0b2da85ebaf75fce1b19f5d645246f3eba Mon Sep 17 00:00:00 2001 From: drh Date: Mon, 22 Apr 2013 23:38:50 +0000 Subject: Fix harmless compiler warnings. FossilOrigin-Name: 1a1cf5aa86734c832d845e07780262a178188d56 --- src/os_unix.c | 1 + 1 file changed, 1 insertion(+) (limited to 'src/os_unix.c') diff --git a/src/os_unix.c b/src/os_unix.c index 8dfbe6181..7448afe4c 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -4782,6 +4782,7 @@ static int unixFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){ */ static int unixUnfetch(sqlite3_file *fd, i64 iOff, void *p){ unixFile *pFd = (unixFile *)fd; /* The underlying database file */ + UNUSED_PARAMETER(iOff); /* If p==0 (unmap the entire file) then there must be no outstanding ** xFetch references. Or, if p!=0 (meaning it is an xFetch reference), -- cgit v1.2.3