laminar_core/xdp/
config.rs1use std::path::PathBuf;
4
5use super::XdpAttachMode;
6
7#[derive(Debug, Clone)]
9pub struct XdpConfig {
10 pub enabled: bool,
12
13 pub bpf_object_path: PathBuf,
15
16 pub interface: String,
18
19 pub port: u16,
21
22 pub attach_mode: XdpAttachMode,
24
25 pub cpu_queue_size: u32,
27
28 pub collect_stats: bool,
30
31 pub fallback_on_error: bool,
33}
34
35impl Default for XdpConfig {
36 fn default() -> Self {
37 Self {
38 enabled: false,
39 bpf_object_path: PathBuf::from("/usr/share/laminardb/laminar_xdp.o"),
40 interface: "eth0".to_string(),
41 port: 9999,
42 attach_mode: XdpAttachMode::Auto,
43 cpu_queue_size: 2048,
44 collect_stats: true,
45 fallback_on_error: true,
46 }
47 }
48}
49
50impl XdpConfig {
51 #[must_use]
53 pub fn builder() -> XdpConfigBuilder {
54 XdpConfigBuilder::default()
55 }
56
57 #[must_use]
80 pub fn auto() -> Self {
81 let caps = crate::detect::SystemCapabilities::detect();
82
83 let attach_mode = if caps.xdp.native_supported {
84 super::XdpAttachMode::Native
85 } else if caps.xdp.generic_supported {
86 super::XdpAttachMode::Generic
87 } else {
88 super::XdpAttachMode::Auto
89 };
90
91 let cpu_queue_size = if caps.memory.total_memory > 32 * 1024 * 1024 * 1024 {
93 4096
94 } else if caps.memory.total_memory > 8 * 1024 * 1024 * 1024 {
95 2048
96 } else {
97 1024
98 };
99
100 Self {
101 enabled: false,
103 bpf_object_path: std::path::PathBuf::from("/usr/share/laminardb/laminar_xdp.o"),
104 interface: "eth0".to_string(),
105 port: 9999,
106 attach_mode,
107 cpu_queue_size,
108 collect_stats: true,
109 fallback_on_error: true,
110 }
111 }
112
113 #[must_use]
117 pub fn xdp_available() -> bool {
118 let caps = crate::detect::SystemCapabilities::detect();
119 caps.xdp.is_usable()
120 }
121
122 pub fn validate(&self) -> Result<(), super::XdpError> {
128 if self.enabled {
129 if self.interface.is_empty() {
130 return Err(super::XdpError::InvalidConfig(
131 "interface cannot be empty".to_string(),
132 ));
133 }
134 if self.port == 0 {
135 return Err(super::XdpError::InvalidConfig(
136 "port must be non-zero".to_string(),
137 ));
138 }
139 if self.cpu_queue_size == 0 {
140 return Err(super::XdpError::InvalidConfig(
141 "cpu_queue_size must be non-zero".to_string(),
142 ));
143 }
144 }
145 Ok(())
146 }
147}
148
149#[derive(Debug, Default)]
151pub struct XdpConfigBuilder {
152 enabled: Option<bool>,
153 bpf_object_path: Option<PathBuf>,
154 interface: Option<String>,
155 port: Option<u16>,
156 attach_mode: Option<XdpAttachMode>,
157 cpu_queue_size: Option<u32>,
158 collect_stats: Option<bool>,
159 fallback_on_error: Option<bool>,
160}
161
162impl XdpConfigBuilder {
163 #[must_use]
165 pub fn enabled(mut self, enabled: bool) -> Self {
166 self.enabled = Some(enabled);
167 self
168 }
169
170 #[must_use]
172 pub fn bpf_object_path(mut self, path: impl Into<PathBuf>) -> Self {
173 self.bpf_object_path = Some(path.into());
174 self
175 }
176
177 #[must_use]
179 pub fn interface(mut self, interface: impl Into<String>) -> Self {
180 self.interface = Some(interface.into());
181 self
182 }
183
184 #[must_use]
186 pub fn port(mut self, port: u16) -> Self {
187 self.port = Some(port);
188 self
189 }
190
191 #[must_use]
193 pub fn attach_mode(mut self, mode: XdpAttachMode) -> Self {
194 self.attach_mode = Some(mode);
195 self
196 }
197
198 #[must_use]
200 pub fn cpu_queue_size(mut self, size: u32) -> Self {
201 self.cpu_queue_size = Some(size);
202 self
203 }
204
205 #[must_use]
207 pub fn collect_stats(mut self, enabled: bool) -> Self {
208 self.collect_stats = Some(enabled);
209 self
210 }
211
212 #[must_use]
214 pub fn fallback_on_error(mut self, enabled: bool) -> Self {
215 self.fallback_on_error = Some(enabled);
216 self
217 }
218
219 pub fn build(self) -> Result<XdpConfig, super::XdpError> {
225 let config = XdpConfig {
226 enabled: self.enabled.unwrap_or(false),
227 bpf_object_path: self
228 .bpf_object_path
229 .unwrap_or_else(|| PathBuf::from("/usr/share/laminardb/laminar_xdp.o")),
230 interface: self.interface.unwrap_or_else(|| "eth0".to_string()),
231 port: self.port.unwrap_or(9999),
232 attach_mode: self.attach_mode.unwrap_or_default(),
233 cpu_queue_size: self.cpu_queue_size.unwrap_or(2048),
234 collect_stats: self.collect_stats.unwrap_or(true),
235 fallback_on_error: self.fallback_on_error.unwrap_or(true),
236 };
237 config.validate()?;
238 Ok(config)
239 }
240}
241
242#[cfg(test)]
243mod tests {
244 use super::*;
245
246 #[test]
247 fn test_default_config() {
248 let config = XdpConfig::default();
249 assert!(!config.enabled);
250 assert_eq!(config.port, 9999);
251 assert_eq!(config.interface, "eth0");
252 assert_eq!(config.attach_mode, XdpAttachMode::Auto);
253 }
254
255 #[test]
256 fn test_builder() {
257 let config = XdpConfig::builder()
258 .enabled(true)
259 .interface("lo")
260 .port(8080)
261 .cpu_queue_size(4096)
262 .build()
263 .unwrap();
264
265 assert!(config.enabled);
266 assert_eq!(config.interface, "lo");
267 assert_eq!(config.port, 8080);
268 assert_eq!(config.cpu_queue_size, 4096);
269 }
270
271 #[test]
272 fn test_validation_empty_interface() {
273 let result = XdpConfig::builder().enabled(true).interface("").build();
274 assert!(result.is_err());
275 }
276
277 #[test]
278 fn test_validation_zero_port() {
279 let result = XdpConfig::builder().enabled(true).port(0).build();
280 assert!(result.is_err());
281 }
282
283 #[test]
284 fn test_disabled_skips_validation() {
285 let config = XdpConfig::builder()
287 .enabled(false)
288 .interface("")
289 .build()
290 .unwrap();
291 assert!(!config.enabled);
292 }
293
294 #[test]
295 fn test_xdp_config_auto() {
296 let config = XdpConfig::auto();
297
298 assert!(!config.enabled);
301
302 assert_eq!(config.interface, "eth0");
304 assert!(config.fallback_on_error);
305 assert!(config.collect_stats);
306 assert!(config.cpu_queue_size >= 1024);
307
308 assert!(config.validate().is_ok());
310 }
311
312 #[test]
313 fn test_xdp_available() {
314 let available = XdpConfig::xdp_available();
316
317 #[cfg(not(all(target_os = "linux", feature = "xdp")))]
319 {
320 assert!(!available);
321 }
322
323 let _ = available;
325 }
326}