Skip to content

Commit

Permalink
Pacing: Pacing of egress QUIC packets
Browse files Browse the repository at this point in the history
  • Loading branch information
Lohith Bellad committed Dec 8, 2020
1 parent 8863dc7 commit 69ae476
Show file tree
Hide file tree
Showing 4 changed files with 373 additions and 0 deletions.
5 changes: 5 additions & 0 deletions include/quiche.h
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,11 @@ ssize_t quiche_conn_recv(quiche_conn *conn, uint8_t *buf, size_t buf_len);
// Writes a single QUIC packet to be sent to the peer.
ssize_t quiche_conn_send(quiche_conn *conn, uint8_t *out, size_t out_len);

// Writes a single QUIC packet to be sent to the peer and fills in the send_time
// of the packet.
ssize_t quiche_conn_send_at(quiche_conn *conn, uint8_t *out, size_t out_len,
struct timespec *send_time);

// Buffer holding data at a specific offset.
typedef struct RangeBuf quiche_rangebuf;

Expand Down
67 changes: 67 additions & 0 deletions src/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ use libc::c_void;
use libc::size_t;
use libc::ssize_t;

#[cfg(unix)]
use libc::timespec;

use crate::*;

#[no_mangle]
Expand Down Expand Up @@ -241,6 +244,11 @@ pub extern fn quiche_config_enable_hystart(config: &mut Config, v: bool) {
config.enable_hystart(v);
}

#[no_mangle]
pub extern fn quiche_config_enable_pacing(config: &mut Config, v: bool) {
config.enable_pacing(v);
}

#[no_mangle]
pub extern fn quiche_config_enable_dgram(
config: &mut Config, enabled: bool, recv_queue_len: size_t,
Expand Down Expand Up @@ -543,6 +551,32 @@ pub extern fn quiche_conn_send(
}
}

#[no_mangle]
#[cfg(unix)]
pub extern fn quiche_conn_send_at(
conn: &mut Connection, out: *mut u8, out_len: size_t,
send_time: &mut timespec,
) -> ssize_t {
if out_len > <ssize_t>::max_value() as usize {
panic!("The provided buffer is too large");
}

let out = unsafe { slice::from_raw_parts_mut(out, out_len) };
let mut schedule_time = time::Instant::now();

let written = match conn.send_at(out, &mut schedule_time) {
Ok(v) => v as ssize_t,

Err(e) => e.to_c(),
};

if written > 0 {
*send_time = instant_to_timespec(schedule_time);
}

written
}

#[no_mangle]
pub extern fn quiche_conn_stream_recv(
conn: &mut Connection, stream_id: u64, out: *mut u8, out_len: size_t,
Expand Down Expand Up @@ -841,3 +875,36 @@ pub extern fn quiche_conn_dgram_purge_outgoing(
pub extern fn quiche_conn_free(conn: *mut Connection) {
unsafe { Box::from_raw(conn) };
}

#[cfg(unix)]
fn instant_to_timespec(instant: std::time::Instant) -> timespec {
use std::hash::{
Hash,
Hasher,
};

struct ToTimeSpec(Vec<i64>);
impl Hasher for ToTimeSpec {
fn finish(&self) -> u64 {
unimplemented!();
}

fn write(&mut self, _: &[u8]) {
unimplemented!();
}

fn write_u64(&mut self, i: u64) {
self.0.push(i as _);
}

fn write_u32(&mut self, i: u32) {
self.0.push(i as _);
}
}
let mut hasher = ToTimeSpec(vec![]);
instant.hash(&mut hasher);
libc::timespec {
tv_sec: hasher.0[0] as _,
tv_nsec: hasher.0[1] as _,
}
}
76 changes: 76 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,8 @@ pub struct Config {

hystart: bool,

pacing: bool,

dgram_recv_max_queue_len: usize,
dgram_send_max_queue_len: usize,

Expand All @@ -497,6 +499,7 @@ impl Config {
grease: true,
cc_algorithm: CongestionControlAlgorithm::CUBIC,
hystart: true,
pacing: true,

dgram_recv_max_queue_len: DEFAULT_MAX_DGRAM_QUEUE_LEN,
dgram_send_max_queue_len: DEFAULT_MAX_DGRAM_QUEUE_LEN,
Expand Down Expand Up @@ -825,6 +828,13 @@ impl Config {
self.dgram_recv_max_queue_len = recv_queue_len;
self.dgram_send_max_queue_len = send_queue_len;
}

/// Configures whether to enable Packet Pacing.
///
/// The default is `true`.
pub fn enable_pacing(&mut self, v: bool) {
self.pacing = v;
}
}

/// A QUIC connection.
Expand Down Expand Up @@ -2831,6 +2841,72 @@ impl Connection {
Ok(written)
}

/// Writes a single QUIC packet to be sent to the peer and fills in
/// send_time of the packet
///
/// On success the number of bytes written to the output buffer is
/// returned, or [`Done`] if there was nothing to write. And send_time
/// is filled with time to send the packet out.
///
/// The application should call `send()` multiple times until [`Done`] is
/// returned, indicating that there are no more packets to send. The time
/// field send_time should be discarded when error is returned. It is
/// recommended that `send()` be called in the following cases:
///
/// * When the application receives QUIC packets from the peer (that is,
/// any time [`recv()`] is also called).
///
/// * When the connection timer expires (that is, any time [`on_timeout()`]
/// is also called).
///
/// * When the application sends data to the peer (for examples, any time
/// [`stream_send()`] or [`stream_shutdown()`] are called).
///
/// [`Done`]: enum.Error.html#variant.Done
/// [`recv()`]: struct.Connection.html#method.recv
/// [`on_timeout()`]: struct.Connection.html#method.on_timeout
/// [`stream_send()`]: struct.Connection.html#method.stream_send
/// [`stream_shutdown()`]: struct.Connection.html#method.stream_shutdown
///
/// ## Examples:
///
/// ```no_run
/// # let mut out = [0; 512];
/// # let socket = std::net::UdpSocket::bind("127.0.0.1:0").unwrap();
/// # let mut config = quiche::Config::new(quiche::PROTOCOL_VERSION)?;
/// # let scid = [0xba; 16];
/// # let mut conn = quiche::accept(&scid, None, &mut config)?;
/// # let mut send_time = std::time::Instant::now();
/// loop {
/// let write = match conn.send_at(&mut out, &mut send_time) {
/// Ok(v) => v,
///
/// Err(quiche::Error::Done) => {
/// // Done writing.
/// break;
/// },
///
/// Err(e) => {
/// // An error occurred, handle it.
/// break;
/// },
/// };
///
/// // pass send_time to kernel using SO_TXTIME
/// socket.send(&out[..write]).unwrap();
/// }
/// # Ok::<(), quiche::Error>(())
/// ```
pub fn send_at(
&mut self, out: &mut [u8], send_time: &mut time::Instant,
) -> Result<usize> {
let written = self.send(out);
if written.is_ok() {
*send_time = self.recovery.get_packet_send_time().unwrap();
}
written
}

// Returns the maximum size of a packet to be sent.
//
// This is a minimum of the sender's and the receiver's maximum UDP payload
Expand Down
Loading

0 comments on commit 69ae476

Please sign in to comment.