8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-23 05:23:03 +01:00

Mostly comments needed after fix for CORE-5041, also added some checks to be safer with repeated I/O

This commit is contained in:
alexpeshkoff 2015-12-15 16:54:23 +00:00
parent d5e5d40b15
commit 8d436826c6
2 changed files with 104 additions and 66 deletions

View File

@ -252,6 +252,7 @@ namespace Jrd {
if (!LCK_convert(tdbb, stateLock, CRYPT_NORMAL,
(flags & CRYPT_HDR_NOWAIT) ? LCK_NO_WAIT : LCK_WAIT))
{
// Failed to take state lock - swith to slow IO mode
slowIO = LCK_read_data(tdbb, stateLock);
fb_assert(slowIO);
}
@ -673,42 +674,48 @@ namespace Jrd {
// Therefore use old (status vector based) method
try
{
if (slowIO)
if (!slowIO)
{
BarSync::LockGuard lockGuard(tdbb, sync);
for (SINT64 previous = slowIO; ; previous = slowIO)
// Normal case (almost always get here)
// Take shared lock on crypto manager and read data
BarSync::IoGuard ioGuard(tdbb, sync);
if (!slowIO)
return internalRead(tdbb, sv, file, bdb, page, noShadows, pageSpace) == SUCCESS_ALL;
}
// Slow IO - we need exclusive lock on crypto manager.
// That may happen only when another process changed DB encyption.
BarSync::LockGuard lockGuard(tdbb, sync);
for (SINT64 previous = slowIO; ; previous = slowIO)
{
switch (internalRead(tdbb, sv, file, bdb, page, noShadows, pageSpace))
{
switch (internalRead(tdbb, sv, file, bdb, page, noShadows, pageSpace))
{
case SUCCESS_ALL:
if (slowIO)
lockAndReadHeader(tdbb, CRYPT_HDR_NOWAIT);
return true;
case SUCCESS_ALL:
if (!slowIO) // if we took a lock last time
return true; // nothing else left to do - IO complete
case FAILED_IO:
return false;
// An attempt to take a lock, if it fails
// we get fresh data from lock needed to validate state of encryption.
// Notice - if lock was taken that's also a kind of state
// change and first time we must proceed with one more read.
lockAndReadHeader(tdbb, CRYPT_HDR_NOWAIT);
if (slowIO == previous) // if crypt state did not change
return true; // we successfully completed IO
break;
case FAILED_CRYPT:
if (slowIO)
{
lockAndReadHeader(tdbb, CRYPT_HDR_NOWAIT);
if (slowIO == previous)
return false;
break;
}
else
return false;
}
case FAILED_IO:
return false; // not related with crypto manager error
case FAILED_CRYPT:
if (!slowIO) // if we took a lock last time
return false; // we can't recover from error here
lockAndReadHeader(tdbb, CRYPT_HDR_NOWAIT);
if (slowIO == previous) // if crypt state did not change
return false; // we can't recover from error here
break;
}
}
else
{
BarSync::IoGuard ioGuard(tdbb, sync);
if (internalRead(tdbb, sv, file, bdb, page, noShadows, pageSpace) != SUCCESS_ALL)
return false;
}
return true;
}
catch (const Exception& ex)
{
@ -768,48 +775,43 @@ namespace Jrd {
// Therefore use old (status vector based) method
try
{
if (slowIO)
if (!slowIO)
{
BarSync::LockGuard lockGuard(tdbb, sync);
for (SINT64 previous = slowIO; ; previous = slowIO)
{
switch (internalWrite(tdbb, sv, file, bdb, page))
{
case SUCCESS_ALL:
if (slowIO)
{
lockAndReadHeader(tdbb, CRYPT_HDR_NOWAIT);
if (slowIO == previous)
return true;
break;
}
else
return true;
// Normal case (almost always get here)
// Take shared lock on crypto manager and write data
BarSync::IoGuard ioGuard(tdbb, sync);
if (!slowIO)
return internalWrite(tdbb, sv, file, bdb, page) == SUCCESS_ALL;
}
case FAILED_IO:
// Have to use slow method - see full comments in read() function
BarSync::LockGuard lockGuard(tdbb, sync);
for (SINT64 previous = slowIO; ; previous = slowIO)
{
switch (internalWrite(tdbb, sv, file, bdb, page))
{
case SUCCESS_ALL:
if (!slowIO)
return true;
lockAndReadHeader(tdbb, CRYPT_HDR_NOWAIT);
if (slowIO == previous)
return true;
break;
case FAILED_IO:
return false;
case FAILED_CRYPT:
if (!slowIO)
return false;
case FAILED_CRYPT:
if (slowIO)
{
lockAndReadHeader(tdbb, CRYPT_HDR_NOWAIT);
if (slowIO == previous)
return false;
break;
}
else
return false;
}
lockAndReadHeader(tdbb, CRYPT_HDR_NOWAIT);
if (slowIO == previous)
return false;
break;
}
}
else
{
BarSync::IoGuard ioGuard(tdbb, sync);
if (internalWrite(tdbb, sv, file, bdb, page) != SUCCESS_ALL)
return false;
}
return true;
}
catch (const Exception& ex)
{

View File

@ -59,6 +59,20 @@ class thread_db;
class Lock;
class PageSpace;
//
// This very specific locking class makes it possible to perform traditional read/write locks,
// but in addition it can on special request perform some predefined action or (in a case when
// >=1 read lock is taken) set barrier for new locks (new locks will not be granted) and
// at the moment when last existing lock is released execute that predefined action in context
// of a thread, releasing last lock.
//
// In our case special request is done from AST handler - and therefore called ast.
// Read locks are done when performing IO+crypt activity - and called ioBegin/ioEnd.
// Write locks are done when some exclusive activity like changing crypt state is
// needed - they are full locks and called lockBegin/lockEnd.
//
class BarSync
{
public:
@ -349,6 +363,28 @@ private:
Database& dbb;
Lock* stateLock;
Lock* threadLock;
// This counter works only in a case when database encryption is changed.
// Traditional processing of AST can not be used for crypto manager.
// The problem is with taking state lock after AST.
// That should be done before next IO operation to guarantee database
// consistency, but IO operation may be requested from another AST
// (database cache blocking) when 'wait for a lock' operations are
// prohibited. I.e. we can't proceed with normal IO without a lock
// but at the same time can't take it.
// The solution is to use crypt versions counter in a lock (incremented
// by one each time one issues ALTER DATABASE ENCRYPT/DECRYPT), read
// it when lock can't be taken, store in slowIO variable, perform IO
// and compare stored value with current lock data. In case when values
// differ encryption status of database was changed during IO operation
// and such operation should be repeated. When data in lock does not
// change during IO that means that crypt rules remained the same even
// without state lock taken by us and therefore result of IO operation
// is correct. As soon as non-waiting attempt to take state lock succeeds
// slowIO mode is off (slowIO counter becomes zero) and we return to
// normal operation.
SINT64 slowIO;
bool crypt, process, down;
};