Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion docs/source/design/tent/slice-spraying.md
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,15 @@ All slice spraying parameters are configurable via the configuration file:
**Notes**:
- Each device's bandwidth is read from the speed and width its port
negotiated (`ibv_query_port`), so a 100G and a 400G NIC in the same host
start from different theoretical rates
start from different theoretical rates. Where libibverbs provides
`ibv_query_port_speed()` (rdma-core >= 62) the *effective* speed it
reports is preferred: for a VF over LAG that is the bandwidth left after
a PF drops out of the bond, which the encoded link rate cannot express.
The verb is resolved as an optional symbol, so older libraries keep
working on the encoded rate. A query *error* keeps the last known
effective speed (falling back would briefly restore the higher encoded
rate on a degraded LAG); failures are counted per device and logged once
per episode
- The theoretical rate seeds the EWMA and bounds it to
`[ewma_min_multiplier, ewma_max_multiplier]` times that rate
- If a device's port speed cannot be read or is outside [min, max],
Expand Down
12 changes: 12 additions & 0 deletions mooncake-common/include/ib_link_speed.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,18 @@ inline double ibLinkSpeedGbps(int active_speed, int active_width) {
return ibLaneSpeedGbps(active_speed) * ibLinkWidthLanes(active_width);
}

// Port speed in Gbps, preferring the effective speed ibv_query_port_speed()
// reports (rdma-core >= 62, here in Mb/s) over the encoded link rate.
// The two differ for a VF over LAG: a PF dropping out of the bond halves
// the VF's bandwidth while its port stays ACTIVE at the same encoding, and
// only the effective speed reflects that. 0 for effective_mbps means the
// verb is unavailable or reported nothing, and the encodings decide.
inline double ibPortSpeedGbps(unsigned long long effective_mbps,
int active_speed, int active_width) {
if (effective_mbps > 0) return effective_mbps / 1000.0;
return ibLinkSpeedGbps(active_speed, active_width);
}

} // namespace mooncake

#endif // MOONCAKE_IB_LINK_SPEED_H_
17 changes: 17 additions & 0 deletions mooncake-common/tests/ib_link_speed_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,23 @@ TEST(IbLinkSpeedTest, ConvertsPortAttrEncodingsToGbps) {

// An encoding the table does not know must not be guessed at: 0 tells the
// caller the speed is unknown so it can fall back explicitly.
// ibv_query_port_speed() (rdma-core >= 62) reports the port's *effective*
// speed in Mb/s: for a VF over LAG that is the bandwidth left
// after a PF drops out, which the encoded link rate cannot express. When
// available it wins; otherwise the encodings decide as before.
TEST(IbLinkSpeedTest, EffectiveSpeedWinsOverEncodedRate) {
// 400G link, but the LAG under this VF is down to one 200G PF.
EXPECT_DOUBLE_EQ(ibPortSpeedGbps(200'000, 128, 2), 200.0);
// Effective speed known, encodings unknown: still usable.
EXPECT_DOUBLE_EQ(ibPortSpeedGbps(100'000, 0, 0), 100.0);
}

TEST(IbLinkSpeedTest, EncodedRateWhenEffectiveSpeedIsUnavailable) {
// 0 = the library predates the verb or the driver reported nothing.
EXPECT_DOUBLE_EQ(ibPortSpeedGbps(0, 128, 2), 400.0);
EXPECT_DOUBLE_EQ(ibPortSpeedGbps(0, 0, 0), 0.0);
}

TEST(IbLinkSpeedTest, UnknownEncodingsReportZero) {
EXPECT_DOUBLE_EQ(ibLinkSpeedGbps(0, 2), 0.0); // speed unset
EXPECT_DOUBLE_EQ(ibLinkSpeedGbps(32, 0), 0.0); // width unset
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class RdmaTransport;
class RdmaContext {
friend class RdmaCQ;
friend class RdmaEndPoint;
friend class RdmaContextTestPeer;

public:
RdmaContext(RdmaTransport &transport);
Expand Down Expand Up @@ -113,7 +114,9 @@ class RdmaContext {
// The one port this context opened; 0 for a slot that never constructed.
uint8_t portNum() const { return params_ ? params_->device.port : 0; }

// Negotiated port speed in Gbps, 0 when it could not be determined.
// Port speed in Gbps: the effective speed from ibv_query_port_speed()
// where the library provides it (LAG-aware), otherwise the negotiated
// link rate. 0 when neither could be determined.
double linkSpeedGbps() const;

// Re-read the port's negotiated speed and width from the hardware, so
Expand All @@ -131,6 +134,18 @@ class RdmaContext {

RdmaParams &params() const { return *params_.get(); }

// True while linkSpeedGbps() is derived from ibv_query_port_speed()
// rather than the encoded speed x width.
bool effectiveSpeedKnown() const {
return effective_speed_mbps_.load(std::memory_order_relaxed) > 0;
}

// ibv_query_port_speed() errors since the device was opened. The verb
// being absent, or reporting 0, is not an error.
uint64_t effectiveSpeedQueryFailures() const {
return effective_speed_query_failures_.load(std::memory_order_relaxed);
}

// PCIe Relaxed Ordering support
bool isRelaxedOrderingEnabled() const { return relaxed_ordering_enabled_; }

Expand All @@ -141,6 +156,12 @@ class RdmaContext {
int openDevice(const std::string &device_name, uint8_t port);
// Decode one ibv_query_port result into active_speed_/active_width_.
void recordPortSpeed(const ibv_port_attr &port_attr);
// Ask ibv_query_port_speed() for the effective speed when the library
// has it; records 0 when the verb is absent or reports nothing, so
// linkSpeedGbps() falls back. A verb *error* keeps the last known value
// instead: on a degraded LAG, falling back would briefly restore the
// higher encoded rate. Failures are counted and logged on transition.
void queryEffectiveSpeed();

// Release every resource currently owned by this context. This is
// intentionally state-independent so it can clean up a partially completed
Expand Down Expand Up @@ -169,6 +190,11 @@ class RdmaContext {
// atomic so a reader added elsewhere stays well-defined.
std::atomic<int> active_speed_{0};
std::atomic<int> active_width_{0};
// From ibv_query_port_speed(), converted to Mb/s; 0 = unavailable.
std::atomic<uint64_t> effective_speed_mbps_{0};
std::atomic<uint64_t> effective_speed_query_failures_{0};
// Whether the last query errored; drives the transition logging.
std::atomic<bool> effective_speed_query_failing_{false};
int gid_index_ = -1;
ibv_gid gid_;

Expand All @@ -184,7 +210,11 @@ class RdmaContext {
// PCIe Relaxed Ordering support
bool relaxed_ordering_enabled_ = false;

const IbvSymbols &verbs_;
// The context's own copy of the loader's verbs table (copied once at
// construction, read-only afterwards). A copy rather than a reference so
// tests can substitute individual entries and drive the port-attribute
// and event paths without an RNIC.
IbvSymbols verbs_;
};

} // namespace tent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ struct IbvSymbols {
int index, union ibv_gid* gid);
int (*ibv_query_port_default)(ibv_context* context, uint8_t port_num,
ibv_port_attr* port_attr);
// Optional (rdma-core >= 62): effective port speed in 100 Mb/s units,
// LAG-aware. nullptr on older libraries; callers fall back to the
// ibv_port_attr encodings.
int (*ibv_query_port_speed)(ibv_context* context, uint32_t port_num,
uint64_t* port_speed);
const char* (*ibv_get_device_name)(struct ibv_device* device);

ibv_pd* (*ibv_alloc_pd)(ibv_context* context);
Expand Down Expand Up @@ -84,4 +89,4 @@ class IbvLoader {

} // namespace tent
} // namespace mooncake
#endif
#endif
42 changes: 40 additions & 2 deletions mooncake-transfer-engine/tent/src/transport/rdma/context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -851,6 +851,7 @@ int RdmaContext::openDevice(const std::string& device_name, uint8_t port) {
native_context_ = context.release();
lid_ = port_attr.lid;
recordPortSpeed(port_attr);
queryEffectiveSpeed();
return 0;
}

Expand All @@ -877,12 +878,49 @@ int RdmaContext::refreshPortAttributes() {
return -1;
}
recordPortSpeed(port_attr);
queryEffectiveSpeed();
return 0;
}

void RdmaContext::queryEffectiveSpeed() {
if (!native_context_ || !verbs_.ibv_query_port_speed) {
effective_speed_mbps_.store(0, std::memory_order_relaxed);
return;
}
uint64_t speed = 0;
int rc = verbs_.ibv_query_port_speed(native_context_, params_->device.port,
&speed);
if (rc != 0) {
// Keep the last known value: on a degraded LAG, dropping to the
// encoded rate would overstate the port until the next successful
// query. Log once per failure episode, not per query.
effective_speed_query_failures_.fetch_add(1, std::memory_order_relaxed);
if (!effective_speed_query_failing_.exchange(
true, std::memory_order_relaxed)) {
LOG(WARNING) << "ibv_query_port_speed failed on " << device_name_
<< " (rc " << rc << "), keeping "
<< effective_speed_mbps_.load(
std::memory_order_relaxed)
<< " Mb/s ("
<< effective_speed_query_failures_.load(
std::memory_order_relaxed)
<< " failures so far)";
}
return;
}
if (effective_speed_query_failing_.exchange(false,
std::memory_order_relaxed)) {
LOG(INFO) << "ibv_query_port_speed recovered on " << device_name_;
}
// The verb reports 100 Mb/s units; store plain Mb/s.
effective_speed_mbps_.store(speed * 100, std::memory_order_relaxed);
}

double RdmaContext::linkSpeedGbps() const {
return ibLinkSpeedGbps(active_speed_.load(std::memory_order_relaxed),
active_width_.load(std::memory_order_relaxed));
return ibPortSpeedGbps(
effective_speed_mbps_.load(std::memory_order_relaxed),
active_speed_.load(std::memory_order_relaxed),
active_width_.load(std::memory_order_relaxed));
}
} // namespace tent
} // namespace mooncake
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,19 @@ bool LoadSymbol(void* handle, const char* name, Fn& out) {
return true;
}

// A symbol newer libibverbs add; its absence must not disable RDMA.
template <typename Fn>
void LoadOptionalSymbol(void* handle, const char* name, Fn& out) {
void* sym = dlsym(handle, name);
if (!sym) {
LOG(INFO) << "libibverbs lacks optional symbol " << name
<< "; the fallback path will be used";
out = nullptr;
return;
}
out = reinterpret_cast<Fn>(sym);
}

IbvLoader& IbvLoader::Instance() {
static IbvLoader instance;
return instance;
Expand All @@ -55,6 +68,8 @@ IbvLoader::IbvLoader() {
ok &= LoadSymbol(handle_, "ibv_query_gid", symbols_.ibv_query_gid);
ok &=
LoadSymbol(handle_, "ibv_query_port", symbols_.ibv_query_port_default);
LoadOptionalSymbol(handle_, "ibv_query_port_speed",
symbols_.ibv_query_port_speed);
ok &= LoadSymbol(handle_, "ibv_get_device_name",
symbols_.ibv_get_device_name);

Expand Down Expand Up @@ -116,4 +131,4 @@ IbvLoader::~IbvLoader() {
}
}
} // namespace tent
} // namespace mooncake
} // namespace mooncake
Original file line number Diff line number Diff line change
Expand Up @@ -914,7 +914,10 @@ void Workers::refreshLinkSpeed(int dev_id, RdmaContext& context) {
// configured default and warns, the same as at startup.
if (after == before) return;
LOG(WARNING) << context.name() << " link speed " << before << " -> "
<< after << " Gbps, re-seeding its bandwidth estimate";
<< after << " Gbps ("
<< (context.effectiveSpeedKnown() ? "effective speed"
: "encoded rate")
<< "), re-seeding its bandwidth estimate";
device_selector_->setDeviceBandwidth(dev_id, after);
}

Expand Down
Loading
Loading