inodes: mount plumbing

Summary:
Now that `mount(2)` complete, we can start plumbing the rest of the mount code
to accomodate for NFS. Trying to find a common ground between Prjfs, FUSE and
Prjfs is harder than I wish it would be and thus I wasn't able to find a
satisfatory solution. For now, I went with a std::variant that stores either a
FuseChannel, or the Nfsd3. In the future once Nfs is stabilized and we have a
good understanding of how it differs (and where it doesn't), EdenMount would
probably need a good refactoring.

Reviewed By: kmancini

Differential Revision: D26500111

fbshipit-source-id: f02a2eaf8890261f150d7cdd2cdfd134aa62c2aa
This commit is contained in:
Xavier Deguillard 2021-02-22 22:38:29 -08:00 committed by Facebook GitHub Bot
parent c47ec0b4aa
commit 79888e20b4
5 changed files with 138 additions and 43 deletions

View File

@ -46,6 +46,7 @@ target_link_libraries(
eden_journal
eden_model_git
eden_nfs_dispatcher
eden_nfs_nfsd3
eden_overlay_thrift_cpp
eden_service_thrift_util
eden_sqlite

View File

@ -636,11 +636,8 @@ folly::Future<folly::Unit> EdenMount::unmount() {
.via(serverState_->getThreadPool().get())
.ensure([this] { channel_.reset(); });
#else
if (serverState_->getReloadableConfig()
.getEdenConfig()
->enableNfsServer.getValue() &&
getConfig()->getMountProtocol() == MountProtocol::NFS) {
// TODO(xavierd): We need to actually do the unmount here.
if (getNfsdChannel() != nullptr) {
XLOG(FATAL) << "Unmount is not yet finished for NFS";
serverState_->getNfsServer()->unregisterMount(getPath());
return folly::makeFuture();
} else {
@ -671,11 +668,22 @@ InodeMetadataTable* EdenMount::getInodeMetadataTable() const {
return overlay_->getInodeMetadataTable();
}
FuseChannel* EdenMount::getFuseChannel() const {
return channel_.get();
FuseChannel* FOLLY_NULLABLE EdenMount::getFuseChannel() const {
if (auto channel = std::get_if<EdenMount::FuseChannelVariant>(&channel_)) {
return channel->get();
}
return nullptr;
}
Nfsd3* FOLLY_NULLABLE EdenMount::getNfsdChannel() const {
if (auto channel = std::get_if<EdenMount::NfsdChannelVariant>(&channel_)) {
return channel->get();
}
return nullptr;
}
#else
PrjfsChannel* EdenMount::getPrjfsChannel() const {
PrjfsChannel* FOLLY_NULLABLE EdenMount::getPrjfsChannel() const {
return channel_.get();
}
#endif
@ -1223,8 +1231,11 @@ folly::Future<TakeoverData::MountInfo> EdenMount::getChannelCompletionFuture() {
#ifndef _WIN32
namespace {
FuseChannel* makeFuseChannel(EdenMount* mount, folly::File fuseFd) {
return new FuseChannel(
std::unique_ptr<FuseChannel, FuseChannelDeleter> makeFuseChannel(
EdenMount* mount,
folly::File fuseFd) {
std::unique_ptr<FuseChannel, FuseChannelDeleter> ret;
ret.reset(new FuseChannel(
std::move(fuseFd),
mount->getPath(),
FLAGS_fuseNumThreads,
@ -1237,7 +1248,8 @@ FuseChannel* makeFuseChannel(EdenMount* mount, folly::File fuseFd) {
.getEdenConfig()
->fuseRequestTimeout.getValue()),
mount->getServerState()->getNotifications(),
mount->getConfig()->getCaseSensitive());
mount->getConfig()->getCaseSensitive()));
return ret;
}
} // namespace
#endif
@ -1324,13 +1336,8 @@ folly::Future<folly::Unit> EdenMount::channelMount(bool readOnly) {
return folly::makeFuture<folly::Unit>(try_.exception());
}
// TODO(xavierd): Do something meaningful once nfsMount
// succeeds, and return the channel.
XLOG(FATAL) << "Mount should have failed but didn't?";
mountPromise->setValue();
// TODO(xavierd): make an NFS channel instead.
channel_.reset(makeFuseChannel(this, folly::File()));
channel_ = std::move(channel);
return makeFuture(folly::unit);
});
});
@ -1371,9 +1378,8 @@ folly::Future<folly::Unit> EdenMount::channelMount(bool readOnly) {
}
mountPromise->setValue();
auto channel =
channel_ =
makeFuseChannel(this, std::move(fuseDevice).value());
channel_.reset(channel);
return folly::makeFuture(folly::unit);
});
}
@ -1396,10 +1402,40 @@ folly::Future<folly::Unit> EdenMount::startChannel(bool readOnly) {
#ifdef _WIN32
channelInitSuccessful(channel_->getStopFuture());
#else
return channel_->initialize().thenValue(
[this](FuseChannel::StopFuture&& fuseCompleteFuture) {
channelInitSuccessful(std::move(fuseCompleteFuture));
});
return std::visit(
[this](auto&& variant) {
using T = std::decay_t<decltype(variant)>;
if constexpr (std::
is_same_v<T, EdenMount::FuseChannelVariant>) {
return variant->initialize().thenValue(
[this](FuseChannel::StopFuture&& fuseCompleteFuture) {
auto stopFuture =
std::move(fuseCompleteFuture)
.deferValue(
[](FuseChannel::StopData&& stopData)
-> EdenMount::ChannelStopData {
return std::move(stopData);
});
channelInitSuccessful(std::move(stopFuture));
});
} else if constexpr (std::is_same_v<
T,
EdenMount::NfsdChannelVariant>) {
auto stopFuture = variant->getStopFuture().deferValue(
[](Nfsd3::StopData&& stopData)
-> EdenMount::ChannelStopData {
return std::move(stopData);
});
channelInitSuccessful(std::move(stopFuture));
return makeFuture(folly::unit);
} else {
static_assert(std::is_same_v<T, std::monostate>);
return EDEN_BUG_FUTURE(folly::Unit)
<< "EdenMount::channel_ is not constructed.";
}
},
channel_);
#endif
})
.thenError([this](folly::exception_wrapper&& ew) {
@ -1451,22 +1487,46 @@ void EdenMount::channelInitSuccessful(
SerializedInodeMap{} // placeholder
));
#else
// If the FUSE device is no longer valid then the mount point has
// been unmounted.
if (!stopData.fuseDevice) {
inodeMap_->setUnmounted();
}
std::visit(
[this](auto&& variant) {
using T = std::decay_t<decltype(variant)>;
std::vector<AbsolutePath> bindMounts;
if constexpr (std::is_same_v<T, EdenMount::FuseStopData>) {
// If the FUSE device is no longer valid then the mount point
// has been unmounted.
if (!variant.fuseDevice) {
inodeMap_->setUnmounted();
}
channelCompletionPromise_.setValue(TakeoverData::MountInfo(
getPath(),
config_->getClientDirectory(),
bindMounts,
std::move(stopData.fuseDevice),
stopData.fuseSettings,
SerializedInodeMap{} // placeholder
));
std::vector<AbsolutePath> bindMounts;
channelCompletionPromise_.setValue(TakeoverData::MountInfo(
getPath(),
config_->getClientDirectory(),
bindMounts,
std::move(variant.fuseDevice),
variant.fuseSettings,
SerializedInodeMap{} // placeholder
));
} else {
static_assert(std::is_same_v<T, EdenMount::NfsdStopData>);
XLOG(FATAL) << "Unmount is not yet finished for NFS";
inodeMap_->setUnmounted();
std::vector<AbsolutePath> bindMounts;
channelCompletionPromise_.setValue(TakeoverData::MountInfo(
getPath(),
config_->getClientDirectory(),
bindMounts,
// TODO(xavierd): the next 2 fields should be a variant too.
folly::File(),
fuse_init_out{},
SerializedInodeMap{} // placeholder
));
}
},
stopData);
#endif
})
.thenError([this](folly::exception_wrapper&& ew) {
@ -1482,9 +1542,15 @@ void EdenMount::takeoverFuse(FuseChannelData takeoverData) {
try {
beginMount().setValue();
channel_.reset(makeFuseChannel(this, std::move(takeoverData.fd)));
auto channel = makeFuseChannel(this, std::move(takeoverData.fd));
auto fuseCompleteFuture =
channel_->initializeFromTakeover(takeoverData.connInfo);
channel->initializeFromTakeover(takeoverData.connInfo)
.deferValue(
[](FuseChannel::StopData&& stopData)
-> EdenMount::ChannelStopData {
return std::move(stopData);
});
channel_ = std::move(channel);
channelInitSuccessful(std::move(fuseCompleteFuture));
} catch (const std::exception&) {
transitionToFuseInitializationErrorState();

View File

@ -35,6 +35,7 @@
#ifndef _WIN32
#include "eden/fs/fuse/FuseChannel.h"
#include "eden/fs/inodes/OverlayFileAccess.h"
#include "eden/fs/nfs/Nfsd3.h"
#else
#include "eden/fs/prjfs/PrjfsChannel.h"
#endif
@ -271,9 +272,10 @@ class EdenMount {
* no internal synchronization of its own.)
*/
#ifdef _WIN32
PrjfsChannel* getPrjfsChannel() const;
PrjfsChannel* FOLLY_NULLABLE getPrjfsChannel() const;
#else
FuseChannel* getFuseChannel() const;
FuseChannel* FOLLY_NULLABLE getFuseChannel() const;
Nfsd3* FOLLY_NULLABLE getNfsdChannel() const;
#endif
ProcessAccessLog& getProcessAccessLog() const {
@ -776,7 +778,9 @@ class EdenMount {
#ifdef _WIN32
using ChannelStopData = PrjfsChannel::StopData;
#else
using ChannelStopData = FuseChannel::StopData;
using FuseStopData = FuseChannel::StopData;
using NfsdStopData = Nfsd3::StopData;
using ChannelStopData = std::variant<FuseStopData, NfsdStopData>;
#endif
using StopFuture = folly::SemiFuture<ChannelStopData>;
@ -959,10 +963,13 @@ class EdenMount {
*/
std::unique_ptr<PrjfsChannel> channel_;
#else
using FuseChannelVariant = std::unique_ptr<FuseChannel, FuseChannelDeleter>;
using NfsdChannelVariant = std::unique_ptr<Nfsd3>;
/**
* The associated fuse channel to the kernel.
*/
std::unique_ptr<FuseChannel, FuseChannelDeleter> channel_;
std::variant<std::monostate, FuseChannelVariant, NfsdChannelVariant> channel_;
#endif // !_WIN32
/**

View File

@ -508,6 +508,17 @@ Nfsd3::Nfsd3(
server_.registerService(kNfsdProgNumber, kNfsd3ProgVersion);
}
}
Nfsd3::~Nfsd3() {
// TODO(xavierd): wait for the pending requests, and the sockets being tore
// down
stopPromise_.setValue(Nfsd3::StopData{});
}
folly::SemiFuture<Nfsd3::StopData> Nfsd3::getStopFuture() {
return stopPromise_.getSemiFuture();
}
} // namespace facebook::eden
#endif

View File

@ -48,6 +48,8 @@ class Nfsd3 {
Notifications* FOLLY_NULLABLE notifications,
bool caseSensitive);
~Nfsd3();
/**
* Obtain the TCP port that this NFSv3 program is listening on.
*/
@ -55,6 +57,13 @@ class Nfsd3 {
return server_.getPort();
}
struct StopData {};
/**
* Return a future that will be triggered on unmount.
*/
folly::SemiFuture<StopData> getStopFuture();
Nfsd3(const Nfsd3&) = delete;
Nfsd3(Nfsd3&&) = delete;
Nfsd3& operator=(const Nfsd3&) = delete;
@ -62,6 +71,7 @@ class Nfsd3 {
private:
RpcServer server_;
folly::Promise<StopData> stopPromise_;
};
} // namespace facebook::eden