Commit 15039642 authored by Phoebe Buckheister's avatar Phoebe Buckheister Committed by Bernd Lietzow

meta: lock moved directory during rename

the ctime of the directory is changed (iff the directory lives on the
local node!), so we must lock it to prevent races with modsync and
rmdir.
parent ee95848d
......@@ -29,6 +29,8 @@ bool UpdateDirParentMsgEx::processIncoming(ResponseContext& ctx)
(void) entryInfo;
(void) parentNodeID;
rctx = &ctx;
BaseType::processIncoming(ctx);
// update operation counters
......@@ -40,6 +42,9 @@ bool UpdateDirParentMsgEx::processIncoming(ResponseContext& ctx)
DirIDLock UpdateDirParentMsgEx::lock(EntryLockStore& store)
{
if (rctx->isLocallyGenerated())
return {};
return {&store, getEntryInfo()->getEntryID(), true};
}
......
......@@ -23,6 +23,8 @@ class UpdateDirParentMsgEx : public MirroredMessage<UpdateDirParentMsg, DirIDLoc
bool isMirrored() override { return getEntryInfo()->getIsBuddyMirrored(); }
private:
ResponseContext* rctx;
bool forwardToSecondary(ResponseContext& ctx) override;
FhgfsOpsErr processSecondaryResponse(NetMessage& resp) override
......
......@@ -25,99 +25,139 @@
#include <boost/scoped_array.hpp>
RenameV2Locks RenameV2MsgEx::lock(EntryLockStore& store)
{
RenameV2Locks result;
namespace {
struct DirHandle {
MetaStore* metaStore;
const EntryInfo* ei;
MetaStore* metaStore = Program::getApp()->getMetaStore();
DirHandle(MetaStore* metaStore, const EntryInfo* ei): metaStore(metaStore), ei(ei) {}
// take care about lock ordering! see MirroredMessage::lock()
// since directories are locked for read, and by the same id as the (parent,name) tuples,the
// same ordering applies.
if (getFromDirInfo()->getEntryID() < getToDirInfo()->getEntryID())
{
result.fromDirLock = {&store, getFromDirInfo()->getEntryID(), true};
result.toDirLock = {&store, getToDirInfo()->getEntryID(), true};
DirHandle(const DirHandle&) = delete;
DirHandle(DirHandle&&) = delete;
result.fromNameLock = {&store, getFromDirInfo()->getEntryID(), getOldName()};
result.toNameLock = {&store, getToDirInfo()->getEntryID(), getNewName()};
}
else if (getFromDirInfo()->getEntryID() == getToDirInfo()->getEntryID())
{
result.fromDirLock = {&store, getFromDirInfo()->getEntryID(), true};
DirHandle& operator=(const DirHandle&) = delete;
DirHandle& operator=(DirHandle&&) = delete;
if (getOldName() < getNewName())
{
result.fromNameLock = {&store, getFromDirInfo()->getEntryID(), getOldName()};
result.toNameLock = {&store, getToDirInfo()->getEntryID(), getNewName()};
}
else if (getOldName() == getNewName())
{
result.fromNameLock = {&store, getFromDirInfo()->getEntryID(), getOldName()};
}
else
{
result.toNameLock = {&store, getToDirInfo()->getEntryID(), getNewName()};
result.fromNameLock = {&store, getFromDirInfo()->getEntryID(), getOldName()};
}
}
else
{
result.toDirLock = {&store, getToDirInfo()->getEntryID(), true};
result.fromDirLock = {&store, getFromDirInfo()->getEntryID(), true};
result.toNameLock = {&store, getToDirInfo()->getEntryID(), getNewName()};
result.fromNameLock = {&store, getFromDirInfo()->getEntryID(), getOldName()};
~DirHandle() {
metaStore->releaseDir(ei->getEntryID());
}
};
}
EntryInfo fromFileInfo;
EntryInfo toFileInfo;
RenameV2Locks RenameV2MsgEx::lock(EntryLockStore& store)
{
MetaStore* metaStore = Program::getApp()->getMetaStore();
DirInode* fromDir = metaStore->referenceDir(getFromDirInfo()->getEntryID(),
getFromDirInfo()->getIsBuddyMirrored(), true);
// if the directory could not be referenced it does not exist on the current node. this will
// cause the operation to fail lateron during executeLocally() when we reference the same
// directory again. since we cannot do anything without having access to the source directory,
// and since no directory with the same id as the source directory can appear after the source
// directory has been removed, we can safely unlock everything right here and continue without
// blocking other workers on the (probably live) target directory.
DirInode* fromDir = metaStore->referenceDir(getFromDirInfo()->getEntryID(),
getFromDirInfo()->getIsBuddyMirrored(), true);
if (!fromDir)
return {};
else
{
const DirHandle _from(metaStore, getFromDirInfo());
DirInode* toDir = metaStore->referenceDir(getToDirInfo()->getEntryID(),
getToDirInfo()->getIsBuddyMirrored(), true);
if (!toDir)
return {};
const DirHandle _to(metaStore, getToDirInfo());
for (;;) {
RenameV2Locks result;
EntryInfo fromFileInfo;
EntryInfo toFileInfo;
fromDir->getFileEntryInfo(getOldName(), fromFileInfo);
if (getFromDirInfo()->getEntryID() == getToDirInfo()->getEntryID())
fromDir->getFileEntryInfo(getNewName(), toFileInfo);
toDir->getFileEntryInfo(getNewName(), toFileInfo);
metaStore->releaseDir(getFromDirInfo()->getEntryID());
}
{
std::map<std::string, DirIDLock*> lockOrder;
if (DirEntryType_ISFILE(fromFileInfo.getEntryType()) && fromFileInfo.getIsInlined())
{
if (DirEntryType_ISFILE(toFileInfo.getEntryType()) && toFileInfo.getIsInlined())
lockOrder.insert(std::make_pair(getFromDirInfo()->getEntryID(), &result.fromDirLock));
lockOrder.insert(std::make_pair(getToDirInfo()->getEntryID(), &result.toDirLock));
if (DirEntryType_ISDIR(fromFileInfo.getEntryType()))
lockOrder.insert(std::make_pair(fromFileInfo.getEntryID(), &result.fromFileLockD));
for (auto it = lockOrder.begin(); it != lockOrder.end(); ++it)
*it->second = {&store, it->first, true};
}
// we might have locked fromFileLockD before fromDirLock due to ordering. resolve the source
// once more and check that we still refer to the same id, otherwise retry until we have the
// correct inode.
// if the name went away we don't have to retry (it can't be created while the dir is locked),
// but retrying is simpler to do.
EntryInfo fromFileInfoCheck;
fromDir->getFileEntryInfo(getOldName(), fromFileInfoCheck);
if (fromFileInfo.getEntryID() != fromFileInfoCheck.getEntryID())
continue;
// take care about lock ordering! see MirroredMessage::lock()
// since directories are locked for read, and by the same id as the (parent,name) tuples,the
// same ordering applies.
if (getFromDirInfo()->getEntryID() < getToDirInfo()->getEntryID())
{
result.fromNameLock = {&store, getFromDirInfo()->getEntryID(), getOldName()};
result.toNameLock = {&store, getToDirInfo()->getEntryID(), getNewName()};
}
else if (getFromDirInfo()->getEntryID() == getToDirInfo()->getEntryID())
{
if (fromFileInfo.getEntryID() < toFileInfo.getEntryID())
if (getOldName() < getNewName())
{
result.fromFileLock = {&store, fromFileInfo.getEntryID()};
result.unlinkedFileLock = {&store, toFileInfo.getEntryID()};
result.fromNameLock = {&store, getFromDirInfo()->getEntryID(), getOldName()};
result.toNameLock = {&store, getToDirInfo()->getEntryID(), getNewName()};
}
else if (fromFileInfo.getEntryID() == toFileInfo.getEntryID())
else if (getOldName() == getNewName())
{
result.fromFileLock = {&store, fromFileInfo.getEntryID()};
result.fromNameLock = {&store, getFromDirInfo()->getEntryID(), getOldName()};
}
else
{
result.unlinkedFileLock = {&store, toFileInfo.getEntryID()};
result.fromFileLock = {&store, fromFileInfo.getEntryID()};
result.toNameLock = {&store, getToDirInfo()->getEntryID(), getNewName()};
result.fromNameLock = {&store, getFromDirInfo()->getEntryID(), getOldName()};
}
}
else
{
result.fromFileLock = {&store, fromFileInfo.getEntryID()};
result.toNameLock = {&store, getToDirInfo()->getEntryID(), getNewName()};
result.fromNameLock = {&store, getFromDirInfo()->getEntryID(), getOldName()};
}
if (DirEntryType_ISFILE(fromFileInfo.getEntryType()) && fromFileInfo.getIsInlined())
{
if (DirEntryType_ISFILE(toFileInfo.getEntryType()) && toFileInfo.getIsInlined())
{
if (fromFileInfo.getEntryID() < toFileInfo.getEntryID())
{
result.fromFileLockF = {&store, fromFileInfo.getEntryID()};
result.unlinkedFileLock = {&store, toFileInfo.getEntryID()};
}
else if (fromFileInfo.getEntryID() == toFileInfo.getEntryID())
{
result.fromFileLockF = {&store, fromFileInfo.getEntryID()};
}
else
{
result.unlinkedFileLock = {&store, toFileInfo.getEntryID()};
result.fromFileLockF = {&store, fromFileInfo.getEntryID()};
}
}
else
{
result.fromFileLockF = {&store, fromFileInfo.getEntryID()};
}
}
}
return result;
return result;
}
}
bool RenameV2MsgEx::processIncoming(ResponseContext& ctx)
......
......@@ -16,7 +16,8 @@ struct RenameV2Locks
DirIDLock toDirLock;
// source file must be locked because concurrent modifications of file attributes may
// race with the moving operation between two servers.
FileIDLock fromFileLock;
FileIDLock fromFileLockF;
DirIDLock fromFileLockD;
// if target exists, the target file must be unlocked to exclude concurrent operations on
// target (eg close, setxattr, ...)
FileIDLock unlinkedFileLock;
......@@ -43,7 +44,8 @@ struct RenameV2Locks
std::swap(toNameLock, other.toNameLock);
std::swap(fromDirLock, other.fromDirLock);
std::swap(toDirLock, other.toDirLock);
std::swap(fromFileLock, other.fromFileLock);
std::swap(fromFileLockF, other.fromFileLockF);
std::swap(fromFileLockD, other.fromFileLockD);
std::swap(unlinkedFileLock, other.unlinkedFileLock);
}
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment