Skip to content

Commit c9f739d

Browse files
authored
Enlarge TCP recv_buf to improve throughput (#6690)
Image uploads performed via the [web console are 3-4x slower than uploads performed via the Oxide CLI](oxidecomputer/console#2096). We found that the CLI creates 8 separate TCP connections to upload the image chunks, while the console uses HTTP/2 to multiplex a single TCP connection six ways. The default TCP `recv_buf` size on Helios is 128 KB, which limits window size and therefore the number of packets that can be sent in parallel. By increasing this value to 1 MB, we can increase single-connection throughput by ~3x, bringing console performance to rough parity with the CLI. This does increase the amount of memory a potential DoS attack could consume, but 1 MB is still quite small relative to the total resources available on a compute sled. While we're at it, also update the TCP congestion control algorithm to `cubic` from its default value of `sunreno`, which may also help improve throughput. Closes #6601
1 parent 3093818 commit c9f739d

File tree

2 files changed

+52
-1
lines changed

2 files changed

+52
-1
lines changed

illumos-utils/src/ipadm.rs

+41-1
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ impl Ipadm {
141141
Ok(Self::addrobj_addr(addrobj)?.is_some())
142142
}
143143

144-
// Set MTU to 9000 on both IPv4 and IPv6
144+
/// Set MTU to 9000 on both IPv4 and IPv6
145145
pub fn set_interface_mtu(datalink: &str) -> Result<(), ExecutionError> {
146146
let mut cmd = std::process::Command::new(PFEXEC);
147147
let cmd = cmd.args(&[
@@ -197,4 +197,44 @@ impl Ipadm {
197197
Self::ensure_ip_addrobj_exists(&addrobj, AddrObjType::DHCP)?;
198198
Ok(())
199199
}
200+
201+
/// Set TCP recv_buf to 1 MB.
202+
pub fn set_tcp_recv_buf() -> Result<(), ExecutionError> {
203+
let mut cmd = std::process::Command::new(PFEXEC);
204+
205+
// This is to improve single-connection throughput on large uploads
206+
// from clients, e.g., images. Modern browsers will almost always use
207+
// HTTP/2, which will multiplex concurrent writes to the same host over
208+
// a single TCP connection. The small default receive window size is a
209+
// major bottleneck, see
210+
// https://github.com/oxidecomputer/console/issues/2096 for further
211+
// details.
212+
let cmd = cmd.args(&[
213+
IPADM,
214+
"set-prop",
215+
"-t",
216+
"-p",
217+
"recv_buf=1000000",
218+
"tcp",
219+
]);
220+
execute(cmd)?;
221+
222+
Ok(())
223+
}
224+
225+
/// Set TCP congestion control algorithm to `cubic`.
226+
pub fn set_tcp_congestion_control() -> Result<(), ExecutionError> {
227+
let mut cmd = std::process::Command::new(PFEXEC);
228+
let cmd = cmd.args(&[
229+
IPADM,
230+
"set-prop",
231+
"-t",
232+
"-p",
233+
"congestion_control=cubic",
234+
"tcp",
235+
]);
236+
execute(cmd)?;
237+
238+
Ok(())
239+
}
200240
}

zone-setup/src/bin/zone-setup.rs

+11
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,17 @@ async fn common_nw_set_up(
563563
Ipadm::set_interface_mtu(&datalink)
564564
.with_context(|| format!("failed to set MTU on datalink {datalink}"))?;
565565

566+
info!(
567+
log, "Setting TCP recv_buf size to 1 MB";
568+
);
569+
Ipadm::set_tcp_recv_buf().context("failed to set TCP recv_buf")?;
570+
571+
info!(
572+
log, "Setting TCP congestion control algorithm to cubic";
573+
);
574+
Ipadm::set_tcp_congestion_control()
575+
.context("failed to set TCP congestion_control")?;
576+
566577
if static_addrs.is_empty() {
567578
info!(
568579
log,

0 commit comments

Comments
 (0)