mirror of
https://github.com/facebook/sapling.git
synced 2024-10-09 16:31:02 +03:00
d8b2d58e84
Summary: We already have AccessType for FUSE, this adds the same categorization for NFS. This allows us to easily filter events in trace stream and ActivityRecorder. Reviewed By: chadaustin Differential Revision: D29771074 fbshipit-source-id: a437f0693f9062fb2df3b6f618a9d8860a05df12
216 lines
6.3 KiB
C++
216 lines
6.3 KiB
C++
/*
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This software may be used and distributed according to the terms of the
|
|
* GNU General Public License version 2.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#ifndef _WIN32
|
|
|
|
// Implementation of the NFSv3 protocol as described in:
|
|
// https://tools.ietf.org/html/rfc1813
|
|
|
|
#include "eden/fs/nfs/NfsDispatcher.h"
|
|
#include "eden/fs/nfs/rpc/Server.h"
|
|
#include "eden/fs/telemetry/TraceBus.h"
|
|
#include "eden/fs/utils/CaseSensitivity.h"
|
|
#include "eden/fs/utils/ProcessAccessLog.h"
|
|
|
|
namespace folly {
|
|
class Executor;
|
|
}
|
|
|
|
namespace facebook::eden {
|
|
|
|
class Notifications;
|
|
class ProcessNameCache;
|
|
|
|
using TraceDetailedArgumentsHandle = std::shared_ptr<void>;
|
|
|
|
struct NfsTraceEvent : TraceEventBase {
|
|
enum Type : unsigned char {
|
|
START,
|
|
FINISH,
|
|
};
|
|
|
|
NfsTraceEvent() = delete;
|
|
|
|
static NfsTraceEvent start(uint32_t xid, uint32_t procNumber) {
|
|
return NfsTraceEvent{
|
|
xid, procNumber, StartDetails{std::unique_ptr<std::string>{}}};
|
|
}
|
|
|
|
static NfsTraceEvent
|
|
start(uint32_t xid, uint32_t procNumber, std::string&& args) {
|
|
return NfsTraceEvent{
|
|
xid, procNumber, StartDetails{std::make_unique<std::string>(args)}};
|
|
}
|
|
|
|
static NfsTraceEvent finish(uint32_t xid, uint32_t procNumber) {
|
|
return NfsTraceEvent{xid, procNumber, FinishDetails{}};
|
|
}
|
|
|
|
Type getType() const {
|
|
return std::holds_alternative<StartDetails>(details_) ? Type::START
|
|
: Type::FINISH;
|
|
}
|
|
|
|
uint32_t getXid() const {
|
|
return xid_;
|
|
}
|
|
|
|
uint32_t getProcNumber() const {
|
|
return procNumber_;
|
|
}
|
|
const std::unique_ptr<std::string>& getArguments() const {
|
|
return std::get<StartDetails>(details_).arguments;
|
|
}
|
|
|
|
private:
|
|
struct StartDetails {
|
|
std::unique_ptr<std::string> arguments;
|
|
};
|
|
|
|
struct FinishDetails {};
|
|
|
|
using Details = std::variant<StartDetails, FinishDetails>;
|
|
|
|
NfsTraceEvent(uint32_t xid, uint32_t procNumber, Details&& details)
|
|
: xid_{xid}, procNumber_{procNumber}, details_{std::move(details)} {}
|
|
|
|
uint32_t xid_;
|
|
uint32_t procNumber_;
|
|
Details details_;
|
|
};
|
|
|
|
class Nfsd3 {
|
|
public:
|
|
/**
|
|
* Create a new RPC NFSv3 program.
|
|
*
|
|
* If registerWithRpcbind is set, this NFSv3 program will advertise itself
|
|
* against the rpcbind daemon allowing it to be visible system wide. Be aware
|
|
* that for a given transport (tcp/udp) only one NFSv3 program can be
|
|
* registered with rpcbind, and thus if a real NFS server is running on this
|
|
* host, EdenFS won't be able to register itself.
|
|
*
|
|
* All the socket processing will be run on the EventBase passed in. This
|
|
* also must be called on that EventBase thread.
|
|
*
|
|
* Note: at mount time, EdenFS will manually call mount.nfs with -o port
|
|
* to manually specify the port on which this server is bound, so registering
|
|
* is not necessary for a properly behaving EdenFS.
|
|
*/
|
|
Nfsd3(
|
|
folly::EventBase* evb,
|
|
std::shared_ptr<folly::Executor> threadPool,
|
|
std::unique_ptr<NfsDispatcher> dispatcher,
|
|
const folly::Logger* straceLogger,
|
|
std::shared_ptr<ProcessNameCache> processNameCache,
|
|
folly::Duration requestTimeout,
|
|
Notifications* FOLLY_NULLABLE notifications,
|
|
CaseSensitivity caseSensitive,
|
|
uint32_t iosize);
|
|
|
|
/**
|
|
* This is triggered when the kernel closes the socket. The socket is closed
|
|
* when the privhelper or a user runs umount.
|
|
*/
|
|
~Nfsd3();
|
|
|
|
void initialize(folly::SocketAddress addr, bool registerWithRpcbind);
|
|
|
|
/**
|
|
* Trigger an invalidation for the given path.
|
|
*
|
|
* To avoid a very large amount of traffic between an NFS client and the
|
|
* server, the client will cache attributes that the server previously
|
|
* returned for a file. This allows stat(2) calls to be fully resolved on the
|
|
* client. However, clients do respect a close-to-open consistency (CTO)
|
|
* whereas opening a file will refresh the client attributes. This invalidate
|
|
* method simply tries to open the given file in a background thread.
|
|
*
|
|
* Note that the open(2) call runs asynchronously in a background thread as
|
|
* both the kernel and EdenFS are holding locks that would otherwise cause
|
|
* EdenFS to deadlock. The flushInvalidations method below should be called
|
|
* with all the locks released to wait for all the invalidation to complete.
|
|
*/
|
|
void invalidate(AbsolutePath path);
|
|
|
|
/**
|
|
* Wait for all pending invalidation to complete.
|
|
*
|
|
* The future will complete when all the previously triggered invalidation
|
|
* completed.
|
|
*/
|
|
folly::Future<folly::Unit> flushInvalidations();
|
|
|
|
/**
|
|
* Obtain the address that this NFSv3 program is listening on.
|
|
*/
|
|
folly::SocketAddress getAddr() const {
|
|
return server_.getAddr();
|
|
}
|
|
|
|
struct StopData {};
|
|
|
|
struct OutstandingRequest {
|
|
uint32_t xid;
|
|
};
|
|
|
|
/**
|
|
* Return a future that will be triggered on unmount.
|
|
*/
|
|
folly::SemiFuture<StopData> getStopFuture();
|
|
|
|
ProcessAccessLog& getProcessAccessLog() {
|
|
return processAccessLog_;
|
|
}
|
|
|
|
Nfsd3(const Nfsd3&) = delete;
|
|
Nfsd3(Nfsd3&&) = delete;
|
|
Nfsd3& operator=(const Nfsd3&) = delete;
|
|
Nfsd3& operator=(Nfsd3&&) = delete;
|
|
|
|
/**
|
|
* Returns the approximate set of outstanding NFS requests. Since
|
|
* telemetry is tracked on a background thread, the result may very slightly
|
|
* lag reality.
|
|
*/
|
|
std::vector<Nfsd3::OutstandingRequest> getOutstandingRequests();
|
|
|
|
/**
|
|
* While the returned handle is alive, NfsTraceEvents published on the
|
|
* TraceBus will have detailed argument strings.
|
|
*/
|
|
TraceDetailedArgumentsHandle traceDetailedArguments();
|
|
|
|
TraceBus<NfsTraceEvent>& getTraceBus() {
|
|
return *traceBus_;
|
|
}
|
|
|
|
private:
|
|
struct TelemetryState {
|
|
std::unordered_map<uint64_t, OutstandingRequest> requests;
|
|
};
|
|
folly::Synchronized<TelemetryState> telemetryState_;
|
|
std::vector<TraceSubscriptionHandle<NfsTraceEvent>> traceSubscriptionHandles_;
|
|
|
|
folly::Promise<StopData> stopPromise_;
|
|
RpcServer server_;
|
|
ProcessAccessLog processAccessLog_;
|
|
folly::Executor::KeepAlive<folly::Executor> invalidationExecutor_;
|
|
std::atomic<size_t> traceDetailedArguments_;
|
|
// The TraceBus must be the last member because its subscribed functions may
|
|
// close over `this` and can run until the TraceBus itself is deallocated.
|
|
std::shared_ptr<TraceBus<NfsTraceEvent>> traceBus_;
|
|
};
|
|
|
|
folly::StringPiece nfsProcName(uint32_t procNumber);
|
|
ProcessAccessLog::AccessType nfsProcAccessType(uint32_t procNumber);
|
|
} // namespace facebook::eden
|
|
|
|
#endif
|