1use super::AdvancedConfig;
4use crate::error::Result;
5#[cfg(target_os = "linux")]
6use crate::error::SaferRingError;
7
8#[cfg(target_os = "linux")]
9use io_uring::{register::Probe, IoUring};
10
11#[derive(Debug, Clone)]
16pub struct FeatureDetector {
17 pub kernel_version: KernelVersion,
19 pub features: AvailableFeatures,
21}
22
23#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
25pub struct KernelVersion {
26 pub major: u32,
28 pub minor: u32,
30 pub patch: u32,
32}
33
34#[derive(Debug, Clone)]
36pub struct AvailableFeatures {
37 pub buffer_selection: bool,
39 pub multi_shot: bool,
41 pub provided_buffers: bool,
43 pub fast_poll: bool,
45 pub sq_poll: bool,
47 pub coop_taskrun: bool,
49 pub defer_taskrun: bool,
51 pub fixed_files: bool,
53 pub fixed_buffers: bool,
55
56 pub advanced_task_work: bool,
59 pub defer_async_work: bool,
61
62 pub send_zc_reporting: bool,
65 pub multishot_completion_batching: bool,
67 pub epoll_uring_wake: bool,
69
70 pub registered_ring_fd: bool,
73
74 pub multishot_timeouts: bool,
77
78 pub user_allocated_ring_memory: bool,
81
82 pub async_cancel_op: bool,
85 pub socket_command_support: bool,
87 pub sysctl_disable_support: bool,
89 pub direct_io_optimizations: bool,
91
92 pub msg_ring_speedup: bool,
95 pub native_bind_listen: bool,
97
98 pub improved_huge_pages: bool,
101 pub async_discard: bool,
103 pub minimum_timeout_waits: bool,
105 pub absolute_timeouts: bool,
107 pub incremental_buffer_consumption: bool,
109 pub registered_buffer_cloning: bool,
111}
112
113impl FeatureDetector {
114 pub fn new() -> Result<Self> {
135 let kernel_version = Self::detect_kernel_version()?;
136 let features = Self::probe_features(&kernel_version);
137
138 Ok(Self {
139 kernel_version,
140 features,
141 })
142 }
143
144 fn detect_kernel_version() -> Result<KernelVersion> {
149 #[cfg(target_os = "linux")]
150 {
151 let mut utsname = unsafe { std::mem::zeroed::<libc::utsname>() };
153 let result = unsafe { libc::uname(&mut utsname) };
154
155 if result != 0 {
156 return Err(SaferRingError::Io(std::io::Error::last_os_error()));
157 }
158
159 let release =
160 unsafe { std::ffi::CStr::from_ptr(utsname.release.as_ptr()).to_string_lossy() };
161
162 let (major, minor, patch) = Self::parse_kernel_version(&release).map_err(|e| {
163 SaferRingError::Io(std::io::Error::new(
164 std::io::ErrorKind::InvalidData,
165 format!("Failed to parse kernel version: {e}"),
166 ))
167 })?;
168
169 Ok(KernelVersion {
170 major,
171 minor,
172 patch,
173 })
174 }
175
176 #[cfg(not(target_os = "linux"))]
177 {
178 Ok(KernelVersion {
180 major: 0,
181 minor: 0,
182 patch: 0,
183 })
184 }
185 }
186
187 #[allow(dead_code)]
192 fn parse_kernel_version(version_str: &str) -> std::result::Result<(u32, u32, u32), String> {
193 let parts: Vec<&str> = version_str.split('.').collect();
194 if parts.len() < 2 {
195 return Err("Invalid kernel version format".to_string());
196 }
197
198 let major = parts[0]
199 .parse()
200 .map_err(|_| "Invalid major version number".to_string())?;
201
202 let minor = parts[1]
203 .parse()
204 .map_err(|_| "Invalid minor version number".to_string())?;
205
206 let patch = if parts.len() > 2 {
207 let patch_str = parts[2]
210 .chars()
211 .take_while(|c| c.is_ascii_digit())
212 .collect::<String>();
213 patch_str.parse().unwrap_or(0)
214 } else {
215 0
216 };
217
218 Ok((major, minor, patch))
219 }
220
221 fn probe_features(kernel_version: &KernelVersion) -> AvailableFeatures {
230 AvailableFeatures {
231 fixed_files: kernel_version
233 >= &KernelVersion {
234 major: 5,
235 minor: 1,
236 patch: 0,
237 },
238 fixed_buffers: kernel_version
239 >= &KernelVersion {
240 major: 5,
241 minor: 1,
242 patch: 0,
243 },
244
245 fast_poll: kernel_version
247 >= &KernelVersion {
248 major: 5,
249 minor: 7,
250 patch: 0,
251 },
252
253 sq_poll: kernel_version
255 >= &KernelVersion {
256 major: 5,
257 minor: 11,
258 patch: 0,
259 },
260
261 buffer_selection: kernel_version
263 >= &KernelVersion {
264 major: 5,
265 minor: 13,
266 patch: 0,
267 },
268 provided_buffers: kernel_version
269 >= &KernelVersion {
270 major: 5,
271 minor: 13,
272 patch: 0,
273 },
274
275 multi_shot: kernel_version
277 >= &KernelVersion {
278 major: 5,
279 minor: 19,
280 patch: 0,
281 },
282
283 coop_taskrun: kernel_version
285 >= &KernelVersion {
286 major: 6,
287 minor: 0,
288 patch: 0,
289 },
290 defer_taskrun: kernel_version
291 >= &KernelVersion {
292 major: 6,
293 minor: 0,
294 patch: 0,
295 },
296
297 advanced_task_work: kernel_version
299 >= &KernelVersion {
300 major: 6,
301 minor: 1,
302 patch: 0,
303 },
304 defer_async_work: kernel_version
305 >= &KernelVersion {
306 major: 6,
307 minor: 1,
308 patch: 0,
309 },
310
311 send_zc_reporting: kernel_version
313 >= &KernelVersion {
314 major: 6,
315 minor: 2,
316 patch: 0,
317 },
318 multishot_completion_batching: kernel_version
319 >= &KernelVersion {
320 major: 6,
321 minor: 2,
322 patch: 0,
323 },
324 epoll_uring_wake: kernel_version
325 >= &KernelVersion {
326 major: 6,
327 minor: 2,
328 patch: 0,
329 },
330
331 registered_ring_fd: kernel_version
333 >= &KernelVersion {
334 major: 6,
335 minor: 3,
336 patch: 0,
337 },
338
339 multishot_timeouts: kernel_version
341 >= &KernelVersion {
342 major: 6,
343 minor: 4,
344 patch: 0,
345 },
346
347 user_allocated_ring_memory: kernel_version
349 >= &KernelVersion {
350 major: 6,
351 minor: 5,
352 patch: 0,
353 },
354
355 async_cancel_op: kernel_version
357 >= &KernelVersion {
358 major: 6,
359 minor: 6,
360 patch: 0,
361 },
362 socket_command_support: kernel_version
363 >= &KernelVersion {
364 major: 6,
365 minor: 6,
366 patch: 0,
367 },
368 sysctl_disable_support: kernel_version
369 >= &KernelVersion {
370 major: 6,
371 minor: 6,
372 patch: 0,
373 },
374 direct_io_optimizations: kernel_version
375 >= &KernelVersion {
376 major: 6,
377 minor: 6,
378 patch: 0,
379 },
380
381 msg_ring_speedup: kernel_version
383 >= &KernelVersion {
384 major: 6,
385 minor: 11,
386 patch: 0,
387 },
388 native_bind_listen: kernel_version
389 >= &KernelVersion {
390 major: 6,
391 minor: 11,
392 patch: 0,
393 },
394
395 improved_huge_pages: kernel_version
397 >= &KernelVersion {
398 major: 6,
399 minor: 12,
400 patch: 0,
401 },
402 async_discard: kernel_version
403 >= &KernelVersion {
404 major: 6,
405 minor: 12,
406 patch: 0,
407 },
408 minimum_timeout_waits: kernel_version
409 >= &KernelVersion {
410 major: 6,
411 minor: 12,
412 patch: 0,
413 },
414 absolute_timeouts: kernel_version
415 >= &KernelVersion {
416 major: 6,
417 minor: 12,
418 patch: 0,
419 },
420 incremental_buffer_consumption: kernel_version
421 >= &KernelVersion {
422 major: 6,
423 minor: 12,
424 patch: 0,
425 },
426 registered_buffer_cloning: kernel_version
427 >= &KernelVersion {
428 major: 6,
429 minor: 12,
430 patch: 0,
431 },
432 }
433 }
434
435 pub fn has_feature(&self, feature: &str) -> bool {
449 match feature {
450 "buffer_selection" => self.features.buffer_selection,
452 "multi_shot" => self.features.multi_shot,
453 "provided_buffers" => self.features.provided_buffers,
454 "fast_poll" => self.features.fast_poll,
455 "sq_poll" => self.features.sq_poll,
456 "coop_taskrun" => self.features.coop_taskrun,
457 "defer_taskrun" => self.features.defer_taskrun,
458 "fixed_files" => self.features.fixed_files,
459 "fixed_buffers" => self.features.fixed_buffers,
460
461 "advanced_task_work" => self.features.advanced_task_work,
463 "defer_async_work" => self.features.defer_async_work,
464
465 "send_zc_reporting" => self.features.send_zc_reporting,
467 "multishot_completion_batching" => self.features.multishot_completion_batching,
468 "epoll_uring_wake" => self.features.epoll_uring_wake,
469
470 "registered_ring_fd" => self.features.registered_ring_fd,
472
473 "multishot_timeouts" => self.features.multishot_timeouts,
475
476 "user_allocated_ring_memory" => self.features.user_allocated_ring_memory,
478
479 "async_cancel_op" => self.features.async_cancel_op,
481 "socket_command_support" => self.features.socket_command_support,
482 "sysctl_disable_support" => self.features.sysctl_disable_support,
483 "direct_io_optimizations" => self.features.direct_io_optimizations,
484
485 "msg_ring_speedup" => self.features.msg_ring_speedup,
487 "native_bind_listen" => self.features.native_bind_listen,
488
489 "improved_huge_pages" => self.features.improved_huge_pages,
491 "async_discard" => self.features.async_discard,
492 "minimum_timeout_waits" => self.features.minimum_timeout_waits,
493 "absolute_timeouts" => self.features.absolute_timeouts,
494 "incremental_buffer_consumption" => self.features.incremental_buffer_consumption,
495 "registered_buffer_cloning" => self.features.registered_buffer_cloning,
496
497 _ => false,
498 }
499 }
500
501 pub fn available_features(&self) -> Vec<&'static str> {
521 let mut features = Vec::new();
522
523 if self.features.fixed_files {
525 features.push("fixed_files");
526 }
527 if self.features.fixed_buffers {
528 features.push("fixed_buffers");
529 }
530 if self.features.fast_poll {
531 features.push("fast_poll");
532 }
533 if self.features.sq_poll {
534 features.push("sq_poll");
535 }
536 if self.features.buffer_selection {
537 features.push("buffer_selection");
538 }
539 if self.features.provided_buffers {
540 features.push("provided_buffers");
541 }
542 if self.features.multi_shot {
543 features.push("multi_shot");
544 }
545 if self.features.coop_taskrun {
546 features.push("coop_taskrun");
547 }
548 if self.features.defer_taskrun {
549 features.push("defer_taskrun");
550 }
551
552 if self.features.advanced_task_work {
554 features.push("advanced_task_work");
555 }
556 if self.features.defer_async_work {
557 features.push("defer_async_work");
558 }
559
560 if self.features.send_zc_reporting {
562 features.push("send_zc_reporting");
563 }
564 if self.features.multishot_completion_batching {
565 features.push("multishot_completion_batching");
566 }
567 if self.features.epoll_uring_wake {
568 features.push("epoll_uring_wake");
569 }
570
571 if self.features.registered_ring_fd {
573 features.push("registered_ring_fd");
574 }
575
576 if self.features.multishot_timeouts {
578 features.push("multishot_timeouts");
579 }
580
581 if self.features.user_allocated_ring_memory {
583 features.push("user_allocated_ring_memory");
584 }
585
586 if self.features.async_cancel_op {
588 features.push("async_cancel_op");
589 }
590 if self.features.socket_command_support {
591 features.push("socket_command_support");
592 }
593 if self.features.sysctl_disable_support {
594 features.push("sysctl_disable_support");
595 }
596 if self.features.direct_io_optimizations {
597 features.push("direct_io_optimizations");
598 }
599
600 if self.features.msg_ring_speedup {
602 features.push("msg_ring_speedup");
603 }
604 if self.features.native_bind_listen {
605 features.push("native_bind_listen");
606 }
607
608 if self.features.improved_huge_pages {
610 features.push("improved_huge_pages");
611 }
612 if self.features.async_discard {
613 features.push("async_discard");
614 }
615 if self.features.minimum_timeout_waits {
616 features.push("minimum_timeout_waits");
617 }
618 if self.features.absolute_timeouts {
619 features.push("absolute_timeouts");
620 }
621 if self.features.incremental_buffer_consumption {
622 features.push("incremental_buffer_consumption");
623 }
624 if self.features.registered_buffer_cloning {
625 features.push("registered_buffer_cloning");
626 }
627
628 features
629 }
630
631 pub fn create_optimal_config(&self) -> AdvancedConfig {
651 AdvancedConfig {
652 buffer_selection: self.features.buffer_selection,
654 multi_shot: self.features.multi_shot,
655 provided_buffers: self.features.provided_buffers,
656 fast_poll: self.features.fast_poll,
657 sq_poll: false, sq_thread_cpu: None,
659 coop_taskrun: self.features.coop_taskrun,
660 defer_taskrun: self.features.defer_taskrun,
661
662 advanced_task_work: self.features.advanced_task_work,
664 defer_async_work: false, send_zc_reporting: self.features.send_zc_reporting,
668 multishot_completion_batching: self.features.multishot_completion_batching,
669 epoll_uring_wake: self.features.epoll_uring_wake,
670
671 registered_ring_fd: self.features.registered_ring_fd,
673
674 multishot_timeouts: self.features.multishot_timeouts,
676
677 user_allocated_ring_memory: false, async_cancel_op: self.features.async_cancel_op,
682 socket_command_support: self.features.socket_command_support,
683 direct_io_optimizations: self.features.direct_io_optimizations,
684
685 msg_ring_speedup: self.features.msg_ring_speedup,
687 native_bind_listen: self.features.native_bind_listen,
688
689 improved_huge_pages: self.features.improved_huge_pages,
691 async_discard: self.features.async_discard,
692 minimum_timeout_waits: self.features.minimum_timeout_waits,
693 absolute_timeouts: self.features.absolute_timeouts,
694 incremental_buffer_consumption: self.features.incremental_buffer_consumption,
695 registered_buffer_cloning: self.features.registered_buffer_cloning,
696 }
697 }
698
699 pub fn new_with_probing() -> Result<Self> {
710 let kernel_version = Self::detect_kernel_version()?;
711 let version_features = Self::probe_features(&kernel_version);
712
713 #[cfg(target_os = "linux")]
714 {
715 match DirectProbeDetector::new() {
717 Ok(probe_detector) => {
718 let probe_features = probe_detector.probe_kernel_features();
719 let features = Self::merge_feature_detection(version_features, probe_features);
720
721 Ok(Self {
722 kernel_version,
723 features,
724 })
725 }
726 Err(_) => {
727 Ok(Self {
729 kernel_version,
730 features: version_features,
731 })
732 }
733 }
734 }
735
736 #[cfg(not(target_os = "linux"))]
737 {
738 Ok(Self {
739 kernel_version,
740 features: version_features,
741 })
742 }
743 }
744
745 #[cfg(target_os = "linux")]
751 fn merge_feature_detection(
752 version_features: AvailableFeatures,
753 probe_features: AvailableFeatures,
754 ) -> AvailableFeatures {
755 AvailableFeatures {
756 fixed_files: version_features.fixed_files || probe_features.fixed_files,
758 fixed_buffers: version_features.fixed_buffers || probe_features.fixed_buffers,
759 fast_poll: version_features.fast_poll || probe_features.fast_poll,
760 sq_poll: version_features.sq_poll || probe_features.sq_poll,
761 buffer_selection: version_features.buffer_selection || probe_features.buffer_selection,
762 provided_buffers: version_features.provided_buffers || probe_features.provided_buffers,
763 multi_shot: version_features.multi_shot || probe_features.multi_shot,
764 coop_taskrun: version_features.coop_taskrun || probe_features.coop_taskrun,
765 defer_taskrun: version_features.defer_taskrun || probe_features.defer_taskrun,
766
767 advanced_task_work: version_features.advanced_task_work,
770 defer_async_work: version_features.defer_async_work,
771 send_zc_reporting: version_features.send_zc_reporting,
772 multishot_completion_batching: version_features.multishot_completion_batching,
773 epoll_uring_wake: version_features.epoll_uring_wake,
774 registered_ring_fd: version_features.registered_ring_fd,
775 multishot_timeouts: version_features.multishot_timeouts,
776 user_allocated_ring_memory: version_features.user_allocated_ring_memory,
777 async_cancel_op: version_features.async_cancel_op,
778 socket_command_support: version_features.socket_command_support,
779 sysctl_disable_support: version_features.sysctl_disable_support,
780 direct_io_optimizations: version_features.direct_io_optimizations,
781 msg_ring_speedup: version_features.msg_ring_speedup,
782 native_bind_listen: version_features.native_bind_listen,
783 improved_huge_pages: version_features.improved_huge_pages,
784 async_discard: version_features.async_discard,
785 minimum_timeout_waits: version_features.minimum_timeout_waits,
786 absolute_timeouts: version_features.absolute_timeouts,
787 incremental_buffer_consumption: version_features.incremental_buffer_consumption,
788 registered_buffer_cloning: version_features.registered_buffer_cloning,
789 }
790 }
791}
792
793#[cfg(target_os = "linux")]
799pub struct DirectProbeDetector {
800 probe: Probe,
801}
802
803#[cfg(target_os = "linux")]
804impl DirectProbeDetector {
805 pub fn new() -> Result<Self> {
838 let ring = IoUring::new(1)?;
839 let mut probe = Probe::new();
840
841 ring.submitter().register_probe(&mut probe)?;
843
844 Ok(Self { probe })
846 }
847
848 pub fn probe_kernel_features(&self) -> AvailableFeatures {
876 AvailableFeatures {
877 fixed_files: self.is_opcode_supported(io_uring::opcode::Read::CODE)
879 && self.is_opcode_supported(io_uring::opcode::Write::CODE),
880 fixed_buffers: self.is_opcode_supported(io_uring::opcode::Read::CODE),
881 fast_poll: self.is_opcode_supported(io_uring::opcode::PollAdd::CODE),
882 sq_poll: true, buffer_selection: self.is_opcode_supported(io_uring::opcode::Recv::CODE), provided_buffers: self.is_opcode_supported(io_uring::opcode::Recv::CODE), multi_shot: self.is_opcode_supported(io_uring::opcode::Accept::CODE), coop_taskrun: false,
890 defer_taskrun: false,
891
892 advanced_task_work: false,
894 defer_async_work: false,
895 send_zc_reporting: self.is_opcode_supported(io_uring::opcode::SendZc::CODE),
896 multishot_completion_batching: false,
897 epoll_uring_wake: false,
898 registered_ring_fd: false,
899 multishot_timeouts: self.is_opcode_supported(io_uring::opcode::Timeout::CODE),
900 user_allocated_ring_memory: false,
901 async_cancel_op: self.is_opcode_supported(io_uring::opcode::AsyncCancel::CODE),
902 socket_command_support: false,
903 sysctl_disable_support: false,
904 direct_io_optimizations: false,
905 msg_ring_speedup: false,
906 native_bind_listen: false,
907 improved_huge_pages: false,
908 async_discard: false,
909 minimum_timeout_waits: false,
910 absolute_timeouts: false,
911 incremental_buffer_consumption: false,
912 registered_buffer_cloning: false,
913 }
914 }
915
916 fn is_opcode_supported(&self, opcode: u8) -> bool {
926 self.probe.is_supported(opcode)
927 }
928}
929
930#[cfg(not(target_os = "linux"))]
932pub struct DirectProbeDetector;
933
934#[cfg(not(target_os = "linux"))]
935impl DirectProbeDetector {
936 pub fn new() -> Result<Self> {
938 use crate::error::SaferRingError;
939 Err(SaferRingError::Io(std::io::Error::new(
940 std::io::ErrorKind::Unsupported,
941 "Direct probing is only available on Linux",
942 )))
943 }
944}
945
946#[cfg(test)]
947mod tests {
948 use super::*;
949
950 #[test]
951 fn test_kernel_version_parsing() {
952 let (major, minor, patch) = FeatureDetector::parse_kernel_version("5.19.0").unwrap();
953 assert_eq!(major, 5);
954 assert_eq!(minor, 19);
955 assert_eq!(patch, 0);
956
957 let (major, minor, patch) = FeatureDetector::parse_kernel_version("6.1.12-arch1").unwrap();
958 assert_eq!(major, 6);
959 assert_eq!(minor, 1);
960 assert_eq!(patch, 12);
961 }
962
963 #[test]
964 fn test_kernel_version_comparison() {
965 let v1 = KernelVersion {
966 major: 5,
967 minor: 19,
968 patch: 0,
969 };
970 let v2 = KernelVersion {
971 major: 6,
972 minor: 0,
973 patch: 0,
974 };
975
976 assert!(v1 < v2);
977 assert!(v2 > v1);
978 assert_eq!(v1, v1);
979 }
980
981 #[test]
982 fn test_feature_detection() {
983 let detector = FeatureDetector::new().unwrap();
984
985 assert!(detector.kernel_version.major > 0 || cfg!(not(target_os = "linux")));
987
988 let _has_buffer_selection = detector.has_feature("buffer_selection");
990 let _available_features = detector.available_features();
991
992 assert!(!detector.has_feature("unknown_feature"));
994 }
995
996 #[test]
997 fn test_feature_version_requirements() {
998 let old_kernel = KernelVersion {
999 major: 5,
1000 minor: 0,
1001 patch: 0,
1002 };
1003 let features = FeatureDetector::probe_features(&old_kernel);
1004
1005 assert!(!features.fixed_files);
1007 assert!(!features.multi_shot);
1008 assert!(!features.buffer_selection);
1009
1010 let new_kernel = KernelVersion {
1011 major: 6,
1012 minor: 5,
1013 patch: 0,
1014 };
1015 let features = FeatureDetector::probe_features(&new_kernel);
1016
1017 assert!(features.fixed_files);
1019 assert!(features.multi_shot);
1020 assert!(features.buffer_selection);
1021 assert!(features.coop_taskrun);
1022 }
1023
1024 #[test]
1025 fn test_kernel_61_features() {
1026 let kernel_61 = KernelVersion {
1027 major: 6,
1028 minor: 1,
1029 patch: 0,
1030 };
1031 let features = FeatureDetector::probe_features(&kernel_61);
1032
1033 assert!(features.advanced_task_work);
1035 assert!(features.defer_async_work);
1036
1037 assert!(!features.send_zc_reporting);
1039 assert!(!features.multishot_completion_batching);
1040 }
1041
1042 #[test]
1043 fn test_kernel_62_features() {
1044 let kernel_62 = KernelVersion {
1045 major: 6,
1046 minor: 2,
1047 patch: 0,
1048 };
1049 let features = FeatureDetector::probe_features(&kernel_62);
1050
1051 assert!(features.advanced_task_work);
1053 assert!(features.defer_async_work);
1054
1055 assert!(features.send_zc_reporting);
1057 assert!(features.multishot_completion_batching);
1058 assert!(features.epoll_uring_wake);
1059
1060 assert!(!features.registered_ring_fd);
1062 }
1063
1064 #[test]
1065 fn test_kernel_66_features() {
1066 let kernel_66 = KernelVersion {
1067 major: 6,
1068 minor: 6,
1069 patch: 0,
1070 };
1071 let features = FeatureDetector::probe_features(&kernel_66);
1072
1073 assert!(features.async_cancel_op);
1075 assert!(features.socket_command_support);
1076 assert!(features.sysctl_disable_support);
1077 assert!(features.direct_io_optimizations);
1078
1079 assert!(!features.msg_ring_speedup);
1081 assert!(!features.native_bind_listen);
1082 }
1083
1084 #[test]
1085 fn test_kernel_612_features() {
1086 let kernel_612 = KernelVersion {
1087 major: 6,
1088 minor: 12,
1089 patch: 0,
1090 };
1091 let features = FeatureDetector::probe_features(&kernel_612);
1092
1093 assert!(features.improved_huge_pages);
1095 assert!(features.async_discard);
1096 assert!(features.minimum_timeout_waits);
1097 assert!(features.absolute_timeouts);
1098 assert!(features.incremental_buffer_consumption);
1099 assert!(features.registered_buffer_cloning);
1100
1101 assert!(features.async_cancel_op);
1103 assert!(features.msg_ring_speedup);
1104 assert!(features.native_bind_listen);
1105 }
1106
1107 #[test]
1108 fn test_has_feature_new_features() {
1109 let detector = FeatureDetector::new().unwrap();
1110
1111 let new_features = vec![
1113 "advanced_task_work",
1114 "defer_async_work",
1115 "send_zc_reporting",
1116 "multishot_completion_batching",
1117 "epoll_uring_wake",
1118 "registered_ring_fd",
1119 "multishot_timeouts",
1120 "user_allocated_ring_memory",
1121 "async_cancel_op",
1122 "socket_command_support",
1123 "sysctl_disable_support",
1124 "direct_io_optimizations",
1125 "msg_ring_speedup",
1126 "native_bind_listen",
1127 "improved_huge_pages",
1128 "async_discard",
1129 "minimum_timeout_waits",
1130 "absolute_timeouts",
1131 "incremental_buffer_consumption",
1132 "registered_buffer_cloning",
1133 ];
1134
1135 for feature in new_features {
1136 let _ = detector.has_feature(feature);
1138 }
1139
1140 assert!(!detector.has_feature("unknown_new_feature"));
1142 }
1143
1144 #[test]
1145 fn test_available_features_includes_new_features() {
1146 let new_kernel = KernelVersion {
1147 major: 6,
1148 minor: 12,
1149 patch: 0,
1150 };
1151 let features = FeatureDetector::probe_features(&new_kernel);
1152 let detector = FeatureDetector {
1153 kernel_version: new_kernel,
1154 features,
1155 };
1156
1157 let available = detector.available_features();
1158
1159 assert!(available.contains(&"advanced_task_work"));
1161 assert!(available.contains(&"async_cancel_op"));
1162 assert!(available.contains(&"improved_huge_pages"));
1163
1164 assert!(available.len() > 10);
1166 }
1167
1168 #[test]
1169 fn test_create_optimal_config_new_features() {
1170 let new_kernel = KernelVersion {
1171 major: 6,
1172 minor: 12,
1173 patch: 0,
1174 };
1175 let features = FeatureDetector::probe_features(&new_kernel);
1176 let detector = FeatureDetector {
1177 kernel_version: new_kernel,
1178 features,
1179 };
1180
1181 let config = detector.create_optimal_config();
1182
1183 assert!(config.advanced_task_work);
1185 assert!(config.async_cancel_op);
1186 assert!(config.improved_huge_pages);
1187
1188 assert!(!config.defer_async_work);
1190 assert!(!config.user_allocated_ring_memory);
1191 }
1192
1193 #[test]
1194 #[cfg(target_os = "linux")]
1195 fn test_new_with_probing_fallback() {
1196 let detector = FeatureDetector::new_with_probing().unwrap();
1198
1199 assert!(detector.kernel_version.major > 0);
1201
1202 let available = detector.available_features();
1204 assert!(!available.is_empty());
1205 }
1206
1207 #[test]
1208 fn test_feature_detection_consistency() {
1209 let kernels = vec![
1211 KernelVersion {
1212 major: 6,
1213 minor: 0,
1214 patch: 0,
1215 },
1216 KernelVersion {
1217 major: 6,
1218 minor: 1,
1219 patch: 0,
1220 },
1221 KernelVersion {
1222 major: 6,
1223 minor: 6,
1224 patch: 0,
1225 },
1226 KernelVersion {
1227 major: 6,
1228 minor: 12,
1229 patch: 0,
1230 },
1231 ];
1232
1233 let mut previous_feature_count = 0;
1234
1235 for kernel in kernels {
1236 let features = FeatureDetector::probe_features(&kernel);
1237 let detector = FeatureDetector {
1238 kernel_version: kernel.clone(),
1239 features,
1240 };
1241 let available = detector.available_features();
1242
1243 assert!(
1245 available.len() >= previous_feature_count,
1246 "Kernel {}.{}.{} has fewer features than previous kernel",
1247 kernel.major,
1248 kernel.minor,
1249 kernel.patch
1250 );
1251
1252 previous_feature_count = available.len();
1253 }
1254 }
1255}