Skip to content

Commit 1e5baf4

Browse files
author
Valentin Obst
committed
rust/kernel: add TCP CCA abstractions
This patch contains the abstractions that facilitate the implementation of CCAs in Rust. They are split into two modules. The `tcp` module contains the elements that are not specific to CCAs, like the wrapper for the `struct tcp_sock`. It is intended to be re-usable by abstractions for other parts of the TCP subsystem. The `tcp::cong` module contains the `module_cca` macro that allows for the convenient declaration of modules that implement a single CCA. It mainly handles the registration and unregistration of the algorithms with the rest of the kernel. The second important part it the `Algorithm` trait, which represents the definition a single CCA. The rest are wrappers around C structures that are specific to the CCA use-case. For example, the `struct sock *` wrapper limits the operations that CCAs can perform on the socket to those that are OK for them to do.
1 parent 8335dc8 commit 1e5baf4

9 files changed

Lines changed: 1223 additions & 0 deletions

File tree

net/ipv4/Kconfig

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,23 @@ config INET_DIAG_DESTROY
466466
had been disconnected.
467467
If unsure, say N.
468468

469+
config RUST_SOCK_ABSTRACTIONS
470+
bool "INET: Rust sock abstractions"
471+
depends on RUST
472+
help
473+
Adds Rust abstractions for working with `struct sock`s.
474+
475+
If unsure, say N.
476+
477+
config RUST_TCP_ABSTRACTIONS
478+
bool "TCP: Rust abstractions"
479+
depends on RUST_SOCK_ABSTRACTIONS
480+
help
481+
Adds support for writing kernel modules that integrate with TCP in
482+
Rust.
483+
484+
If unsure, say N.
485+
469486
menuconfig TCP_CONG_ADVANCED
470487
bool "TCP: advanced congestion control"
471488
help

rust/bindings/bindings_helper.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <linux/slab.h>
1818
#include <linux/wait.h>
1919
#include <linux/workqueue.h>
20+
#include <net/tcp.h>
2021

2122
/* `bindgen` gets confused at certain things. */
2223
const size_t RUST_CONST_HELPER_ARCH_SLAB_MINALIGN = ARCH_SLAB_MINALIGN;

rust/helpers.c

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include <linux/spinlock.h>
3232
#include <linux/wait.h>
3333
#include <linux/workqueue.h>
34+
#include <net/tcp.h>
3435

3536
__noreturn void rust_helper_BUG(void)
3637
{
@@ -157,6 +158,42 @@ void rust_helper_init_work_with_key(struct work_struct *work, work_func_t func,
157158
}
158159
EXPORT_SYMBOL_GPL(rust_helper_init_work_with_key);
159160

161+
bool rust_helper_tcp_in_slow_start(const struct tcp_sock *tp)
162+
{
163+
return tcp_in_slow_start(tp);
164+
}
165+
EXPORT_SYMBOL_GPL(rust_helper_tcp_in_slow_start);
166+
167+
bool rust_helper_tcp_is_cwnd_limited(const struct sock *sk)
168+
{
169+
return tcp_is_cwnd_limited(sk);
170+
}
171+
EXPORT_SYMBOL_GPL(rust_helper_tcp_is_cwnd_limited);
172+
173+
struct tcp_sock *rust_helper_tcp_sk(struct sock *sk)
174+
{
175+
return tcp_sk(sk);
176+
}
177+
EXPORT_SYMBOL_GPL(rust_helper_tcp_sk);
178+
179+
u32 rust_helper_tcp_snd_cwnd(const struct tcp_sock *tp)
180+
{
181+
return tcp_snd_cwnd(tp);
182+
}
183+
EXPORT_SYMBOL_GPL(rust_helper_tcp_snd_cwnd);
184+
185+
struct inet_connection_sock *rust_helper_inet_csk(const struct sock *sk)
186+
{
187+
return inet_csk(sk);
188+
}
189+
EXPORT_SYMBOL_GPL(rust_helper_inet_csk);
190+
191+
void *rust_helper_inet_csk_ca(struct sock *sk)
192+
{
193+
return inet_csk_ca(sk);
194+
}
195+
EXPORT_SYMBOL_GPL(rust_helper_inet_csk_ca);
196+
160197
/*
161198
* `bindgen` binds the C `size_t` type as the Rust `usize` type, so we can
162199
* use it in contexts where Rust expects a `usize` like slice (array) indices.

rust/kernel/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
1414
#![no_std]
1515
#![feature(allocator_api)]
16+
#![feature(associated_type_bounds)]
1617
#![feature(coerce_unsized)]
1718
#![feature(dispatch_from_dyn)]
1819
#![feature(new_uninit)]

rust/kernel/net.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,7 @@
44
55
#[cfg(CONFIG_RUST_PHYLIB_ABSTRACTIONS)]
66
pub mod phy;
7+
#[cfg(CONFIG_RUST_SOCK_ABSTRACTIONS)]
8+
pub mod sock;
9+
#[cfg(CONFIG_RUST_TCP_ABSTRACTIONS)]
10+
pub mod tcp;

rust/kernel/net/sock.rs

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
//! Representation of a C `struct sock`.
2+
3+
#[cfg(CONFIG_RUST_TCP_ABSTRACTIONS)]
4+
use crate::net::tcp::{self, InetConnectionSock, TcpSock};
5+
use crate::types::Opaque;
6+
use core::convert::TryFrom;
7+
use core::ptr::addr_of;
8+
9+
/// Representation of a C `struct sock`.
10+
///
11+
/// Not intended to be used directly by modules. Abstractions should provide a
12+
/// safe interface to only those methods that are OK to use for the module.
13+
#[repr(transparent)]
14+
pub(crate) struct Sock {
15+
sk: Opaque<bindings::sock>,
16+
}
17+
18+
impl Sock {
19+
/// Returns a raw pointer to the wrapped `struct sock`.
20+
///
21+
/// It is up to the caller to use it correctly.
22+
#[inline]
23+
pub(crate) fn raw_sk_mut(&mut self) -> *mut bindings::sock {
24+
self.sk.get()
25+
}
26+
27+
/// Returns the sockets pacing rate in bytes per second.
28+
#[inline]
29+
pub(crate) fn sk_pacing_rate(&self) -> u64 {
30+
// SAFETY: This field can always be read. It is a C unsigned long so we
31+
// can always convert it to a u64 without loss.
32+
// TODO: Make READ_ONCE once it is available.
33+
unsafe { *addr_of!((*self.sk.get()).sk_pacing_rate) as u64 }
34+
}
35+
36+
/// Returns the sockets pacing status.
37+
#[inline]
38+
pub(crate) fn sk_pacing_status(&self) -> Result<Pacing, ()> {
39+
// SAFETY: This field can always be read.
40+
unsafe { Pacing::try_from(*addr_of!((*self.sk.get()).sk_pacing_status)) }
41+
}
42+
43+
/// Returns the sockets maximum GSO segment size to build.
44+
#[inline]
45+
pub(crate) fn sk_gso_max_size(&self) -> u32 {
46+
// SAFETY: This field can always be read. It is an unsigned int and we
47+
// are guaranteed that this will always fit into a u32.
48+
unsafe { *addr_of!((*self.sk.get()).sk_gso_max_size) as u32 }
49+
}
50+
51+
/// Returns the [`TcpSock`] that is containing the `Sock`.
52+
///
53+
/// # Safety
54+
///
55+
/// `sk` must be valid for `tcp_sk`.
56+
#[inline]
57+
#[cfg(CONFIG_RUST_TCP_ABSTRACTIONS)]
58+
pub(crate) unsafe fn tcp_sk<'a>(&'a self) -> &'a TcpSock {
59+
// SAFETY:
60+
// - Downcasting via `tcp_sk` is OK by the functions precondition.
61+
// - The cast is OK since `TcpSock` is transparent to `struct tcp_sock`.
62+
unsafe { &*(bindings::tcp_sk(self.sk.get()) as *const TcpSock) }
63+
}
64+
65+
/// Returns the [`TcpSock`] that is containing the `Sock`.
66+
///
67+
/// # Safety
68+
///
69+
/// `sk` must be valid for `tcp_sk`.
70+
#[inline]
71+
#[cfg(CONFIG_RUST_TCP_ABSTRACTIONS)]
72+
pub(crate) unsafe fn tcp_sk_mut<'a>(&'a mut self) -> &'a mut TcpSock {
73+
// SAFETY:
74+
// - Downcasting via `tcp_sk` is OK by the functions precondition.
75+
// - The cast is OK since `TcpSock` is transparent to `struct tcp_sock`.
76+
unsafe { &mut *(bindings::tcp_sk(self.sk.get()) as *mut TcpSock) }
77+
}
78+
79+
/// Returns the [private data] of the instance of the CCA used by this
80+
/// socket.
81+
///
82+
/// [private data]: tcp::cong::Algorithm::Data
83+
///
84+
/// # Safety
85+
///
86+
/// - `sk` must be valid for `inet_csk_ca`,
87+
/// - `sk` must use the CCA `T`, the `init` CB of the CCA must have been
88+
/// called, the `release` CB of the CCA must not have been called.
89+
#[inline]
90+
#[cfg(CONFIG_RUST_TCP_ABSTRACTIONS)]
91+
pub(crate) unsafe fn inet_csk_ca<'a, T: tcp::cong::Algorithm + ?Sized>(
92+
&'a self,
93+
) -> &'a T::Data {
94+
// SAFETY: By the function's preconditions, calling `inet_csk_ca` is OK
95+
// and the returned pointer points to a valid instance of `T::Data`.
96+
unsafe { &*(bindings::inet_csk_ca(self.sk.get()) as *const T::Data) }
97+
}
98+
99+
/// Returns the [private data] of the instance of the CCA used by this
100+
/// socket.
101+
///
102+
/// [private data]: tcp::cong::Algorithm::Data
103+
///
104+
/// # Safety
105+
///
106+
/// - `sk` must be valid for `inet_csk_ca`,
107+
/// - `sk` must use the CCA `T`, the `init` CB of the CCA must have been
108+
/// called, the `release` CB of the CCA must not have been called.
109+
#[inline]
110+
#[cfg(CONFIG_RUST_TCP_ABSTRACTIONS)]
111+
pub(crate) unsafe fn inet_csk_ca_mut<'a, T: tcp::cong::Algorithm + ?Sized>(
112+
&'a mut self,
113+
) -> &'a mut T::Data {
114+
// SAFETY: By the function's preconditions, calling `inet_csk_ca` is OK
115+
// and the returned pointer points to a valid instance of `T::Data`.
116+
unsafe { &mut *(bindings::inet_csk_ca(self.sk.get()) as *mut T::Data) }
117+
}
118+
119+
/// Returns the [`InetConnectionSock`] view of this socket.
120+
///
121+
/// # Safety
122+
///
123+
/// `sk` must be valid for `inet_csk`.
124+
#[inline]
125+
#[cfg(CONFIG_RUST_TCP_ABSTRACTIONS)]
126+
pub(crate) unsafe fn inet_csk<'a>(&'a self) -> &'a InetConnectionSock {
127+
// SAFETY:
128+
// - Calling `inet_csk` is OK by the functions precondition.
129+
// - The cast is OK since `InetConnectionSock` is transparent to
130+
// `struct inet_connection_sock`.
131+
unsafe { &*(bindings::inet_csk(self.sk.get()) as *const InetConnectionSock) }
132+
}
133+
134+
/// Tests if the connection's sending rate is limited by the cwnd.
135+
///
136+
/// # Safety
137+
///
138+
/// `sk` must be valid for `tcp_is_cwnd_limited`.
139+
#[inline]
140+
#[cfg(CONFIG_RUST_TCP_ABSTRACTIONS)]
141+
pub(crate) unsafe fn tcp_is_cwnd_limited(&self) -> bool {
142+
// SAFETY: Calling `tcp_is_cwnd_limited` is OK by the functions
143+
// precondition.
144+
unsafe { bindings::tcp_is_cwnd_limited(self.sk.get()) }
145+
}
146+
}
147+
148+
/// The socket's pacing status.
149+
#[repr(u32)]
150+
pub enum Pacing {
151+
/// TODO
152+
r#None = bindings::sk_pacing_SK_PACING_NONE,
153+
/// TODO
154+
Needed = bindings::sk_pacing_SK_PACING_NEEDED,
155+
/// TODO
156+
Fq = bindings::sk_pacing_SK_PACING_FQ,
157+
}
158+
159+
impl TryFrom<u32> for Pacing {
160+
type Error = ();
161+
162+
fn try_from(val: u32) -> Result<Self, Self::Error> {
163+
match val {
164+
x if x == Pacing::r#None as u32 => Ok(Pacing::r#None),
165+
x if x == Pacing::Needed as u32 => Ok(Pacing::Needed),
166+
x if x == Pacing::Fq as u32 => Ok(Pacing::Fq),
167+
_ => Err(()),
168+
}
169+
}
170+
}

0 commit comments

Comments
 (0)