quic/common/TransportKnobs.cpp (123 lines of code) (raw):
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#include <folly/json.h>
#include <glog/logging.h>
#include <quic/QuicConstants.h>
#include <quic/common/TransportKnobs.h>
namespace quic {
namespace {
constexpr uint64_t kKnobFractionMax = 100;
bool compareTransportKnobParam(
const TransportKnobParam& lhs,
const TransportKnobParam& rhs) {
// Sort param by id, then value
if (lhs.id != rhs.id) {
return lhs.id < rhs.id;
}
return lhs.val < rhs.val;
}
} // namespace
folly::Optional<TransportKnobParams> parseTransportKnobs(
const std::string& serializedParams) {
TransportKnobParams knobParams;
try {
folly::dynamic params = folly::parseJson(serializedParams);
for (const auto& id : params.keys()) {
auto paramId = folly::to<uint64_t>(id.asInt());
auto val = params[id];
switch (val.type()) {
case folly::dynamic::Type::BOOL:
case folly::dynamic::Type::INT64:
knobParams.push_back({paramId, folly::to<uint64_t>(val.asInt())});
continue;
case folly::dynamic::Type::STRING: {
/*
* set cc algorithm
* expected format: string, all lower case, name of cc algorithm
*/
if (paramId ==
static_cast<uint64_t>(TransportKnobParamId::CC_ALGORITHM_KNOB)) {
folly::Optional<CongestionControlType> cctype =
congestionControlStrToType(val.asString());
if (cctype) {
knobParams.push_back(
{paramId, folly::to<uint64_t>(cctype.value())});
} else {
LOG(ERROR) << "unknown cc type " << val;
return folly::none;
}
/*
* set rtt factor used in cc algs like bbr or copa
* expressed as a fraction (see
* quic/congestion_control/TokenlessPacer.cpp) expected format:
* string, "{numerator}/{denominator}" numerator and denominator
* must both be in the range (0,MAX]
*/
} else if (
paramId ==
static_cast<uint64_t>(
TransportKnobParamId::STARTUP_RTT_FACTOR_KNOB) ||
paramId ==
static_cast<uint64_t>(
TransportKnobParamId::DEFAULT_RTT_FACTOR_KNOB)) {
auto s = val.asString();
uint64_t factor = 0;
auto pos = s.find('/');
if (pos == std::string::npos) {
LOG(ERROR)
<< "rtt factor knob expected format {numerator}/{denominator}";
return folly::none;
}
uint64_t numerator =
folly::tryTo<int>(s.substr(0, pos)).value_or(kKnobFractionMax);
uint64_t denominator =
folly::tryTo<int>(s.substr(pos + 1, s.length()))
.value_or(kKnobFractionMax);
if (numerator <= 0 || denominator <= 0 ||
numerator >= kKnobFractionMax ||
denominator >= kKnobFractionMax) {
LOG(ERROR)
<< "rtt factor knob numerator and denominator must be ints in range (0,"
<< kKnobFractionMax << "]";
return folly::none;
}
// transport knobs must be a single int, so we pack numerator and
// denominator into a single int here and unpack in the handler
factor = numerator * kKnobFractionMax + denominator;
knobParams.push_back({paramId, folly::to<uint64_t>(factor)});
} else if (
paramId ==
static_cast<uint64_t>(
TransportKnobParamId::AUTO_BACKGROUND_MODE)) {
/*
* set the auto background mode parameters for the transport
* expected format: string
* "{priority_threshold},{percent_utilization}" priority_threshold:
* integer value [0-7] percent_utilization: integer value [25-100]
*/
uint64_t combinedKnobVal = 0;
std::string priorityThresholdStr, utilizationPercentStr;
if (!folly::split(
',',
val.asString(),
priorityThresholdStr,
utilizationPercentStr)) {
LOG(ERROR)
<< "auto background mode knob value is not in expected format: "
<< "{priority_threshold},{percent_utilization}";
return folly::none;
}
uint64_t priorityThreshold =
folly::tryTo<uint64_t>(priorityThresholdStr)
.value_or(kDefaultMaxPriority + 1);
uint64_t utilizationPercent =
folly::tryTo<uint64_t>(utilizationPercentStr).value_or(101);
if (priorityThreshold > kDefaultMaxPriority ||
utilizationPercent < 25 || utilizationPercent > 100) {
LOG(ERROR) << "invalid auto background mode parameters."
<< "priority_threshold must be int [0-7]. "
<< "percent_utilization must be int [25-100]";
return folly::none;
}
// pack the values into one integer that will be unpacked in the
// handler
combinedKnobVal =
(priorityThreshold * kPriorityThresholdKnobMultiplier) +
utilizationPercent;
knobParams.push_back(
{paramId, folly::to<uint64_t>(combinedKnobVal)});
} else {
LOG(ERROR) << "string param type is not valid for this knob";
return folly::none;
}
continue;
}
default:
// Quic transport knob param values cannot be of type ARRAY, NULLT or
// OBJECT
LOG(ERROR) << "Invalid transport knob param value type" << val.type();
return folly::none;
}
}
} catch (const std::exception& e) {
LOG(ERROR) << "fail to parse knobs: " << e.what();
return folly::none;
}
std::sort(knobParams.begin(), knobParams.end(), compareTransportKnobParam);
return knobParams;
}
} // namespace quic