Skip to main content

safer_ring/advanced/
feature_detection.rs

1//! Kernel feature detection and capability probing.
2
3use 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/// Kernel feature detection and capability probing.
12///
13/// Provides methods to detect which advanced io_uring features
14/// are available on the current kernel version.
15#[derive(Debug, Clone)]
16pub struct FeatureDetector {
17    /// Detected kernel version
18    pub kernel_version: KernelVersion,
19    /// Available features
20    pub features: AvailableFeatures,
21}
22
23/// Kernel version information.
24#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
25pub struct KernelVersion {
26    /// Major version number
27    pub major: u32,
28    /// Minor version number
29    pub minor: u32,
30    /// Patch version number
31    pub patch: u32,
32}
33
34/// Available io_uring features on this kernel.
35#[derive(Debug, Clone)]
36pub struct AvailableFeatures {
37    /// Buffer selection is supported
38    pub buffer_selection: bool,
39    /// Multi-shot operations are supported
40    pub multi_shot: bool,
41    /// Provided buffers are supported
42    pub provided_buffers: bool,
43    /// Fast poll is supported
44    pub fast_poll: bool,
45    /// SQ polling is supported
46    pub sq_poll: bool,
47    /// Cooperative task running is supported
48    pub coop_taskrun: bool,
49    /// Task work defer is supported
50    pub defer_taskrun: bool,
51    /// Fixed files are supported
52    pub fixed_files: bool,
53    /// Fixed buffers are supported
54    pub fixed_buffers: bool,
55
56    // Kernel 6.1+ features
57    /// Advanced task work management (6.1+)
58    pub advanced_task_work: bool,
59    /// Deferred async work until GETEVENTS (6.1+)
60    pub defer_async_work: bool,
61
62    // Kernel 6.2+ features
63    /// Zero-copy send reporting with SEND_ZC_REPORT_USAGE (6.2+)
64    pub send_zc_reporting: bool,
65    /// Completion batching for multishot operations (6.2+)
66    pub multishot_completion_batching: bool,
67    /// EPOLL_URING_WAKE support (6.2+)
68    pub epoll_uring_wake: bool,
69
70    // Kernel 6.3+ features
71    /// io_uring_register() with registered ring fd (6.3+)
72    pub registered_ring_fd: bool,
73
74    // Kernel 6.4+ features
75    /// Multishot timeout operations (6.4+)
76    pub multishot_timeouts: bool,
77
78    // Kernel 6.5+ features
79    /// User-allocated ring memory (6.5+)
80    pub user_allocated_ring_memory: bool,
81
82    // Kernel 6.6+ features
83    /// Async operation cancellation with IORING_ASYNC_CANCEL_OP (6.6+)
84    pub async_cancel_op: bool,
85    /// io_uring command support for sockets (6.6+)
86    pub socket_command_support: bool,
87    /// System-wide io_uring disable sysctl (6.6+)
88    pub sysctl_disable_support: bool,
89    /// Direct I/O performance optimizations (6.6+)
90    pub direct_io_optimizations: bool,
91
92    // Kernel 6.11+ features
93    /// MSG_RING performance improvements (6.11+)
94    pub msg_ring_speedup: bool,
95    /// Native bind/listen operations (6.11+)
96    pub native_bind_listen: bool,
97
98    // Kernel 6.12+ features
99    /// Improved huge page segment handling (6.12+)
100    pub improved_huge_pages: bool,
101    /// Fully asynchronous discard operations (6.12+)
102    pub async_discard: bool,
103    /// Minimum timeout waits with dual conditions (6.12+)
104    pub minimum_timeout_waits: bool,
105    /// Absolute timeouts with multiple clock sources (6.12+)
106    pub absolute_timeouts: bool,
107    /// Incremental provided buffer consumption (6.12+)
108    pub incremental_buffer_consumption: bool,
109    /// Registered buffer cloning across threads (6.12+)
110    pub registered_buffer_cloning: bool,
111}
112
113impl FeatureDetector {
114    /// Create a new feature detector and probe the kernel.
115    ///
116    /// Detects the kernel version and probes for available features.
117    ///
118    /// # Errors
119    ///
120    /// Returns an error if kernel version detection fails.
121    ///
122    /// # Examples
123    ///
124    /// ```rust,no_run
125    /// use safer_ring::advanced::FeatureDetector;
126    ///
127    /// let detector = FeatureDetector::new()?;
128    /// println!("Kernel: {}.{}.{}",
129    ///     detector.kernel_version.major,
130    ///     detector.kernel_version.minor,
131    ///     detector.kernel_version.patch);
132    /// # Ok::<(), safer_ring::error::SaferRingError>(())
133    /// ```
134    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    /// Detect the current kernel version.
145    ///
146    /// Uses the `uname` syscall on Linux to retrieve kernel version information.
147    /// On non-Linux platforms, returns a minimal version (0.0.0).
148    fn detect_kernel_version() -> Result<KernelVersion> {
149        #[cfg(target_os = "linux")]
150        {
151            // Use libc to get kernel version via uname syscall
152            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            // Return minimal version for non-Linux platforms
179            Ok(KernelVersion {
180                major: 0,
181                minor: 0,
182                patch: 0,
183            })
184        }
185    }
186
187    /// Parse kernel version string into components.
188    ///
189    /// Handles various kernel version formats including those with
190    /// additional suffixes like "-arch1" or "-generic".
191    #[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            // Extract numeric part before any non-numeric characters
208            // This handles cases like "12-arch1" -> 12
209            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    /// Probe for available features based on kernel version.
222    ///
223    /// Feature availability is determined by when they were introduced
224    /// in the Linux kernel. This is a conservative approach that may
225    /// miss backported features but ensures compatibility.
226    ///
227    /// Uses kernel version comparison to determine which features should
228    /// be available based on historical kernel development.
229    fn probe_features(kernel_version: &KernelVersion) -> AvailableFeatures {
230        AvailableFeatures {
231            // Basic io_uring support (5.1+)
232            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 (5.7+)
246            fast_poll: kernel_version
247                >= &KernelVersion {
248                    major: 5,
249                    minor: 7,
250                    patch: 0,
251                },
252
253            // SQ polling (5.11+)
254            sq_poll: kernel_version
255                >= &KernelVersion {
256                    major: 5,
257                    minor: 11,
258                    patch: 0,
259                },
260
261            // Buffer selection (5.13+)
262            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 operations (5.19+)
276            multi_shot: kernel_version
277                >= &KernelVersion {
278                    major: 5,
279                    minor: 19,
280                    patch: 0,
281                },
282
283            // Cooperative task running (6.0+)
284            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            // Kernel 6.1+ features
298            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            // Kernel 6.2+ features
312            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            // Kernel 6.3+ features
332            registered_ring_fd: kernel_version
333                >= &KernelVersion {
334                    major: 6,
335                    minor: 3,
336                    patch: 0,
337                },
338
339            // Kernel 6.4+ features
340            multishot_timeouts: kernel_version
341                >= &KernelVersion {
342                    major: 6,
343                    minor: 4,
344                    patch: 0,
345                },
346
347            // Kernel 6.5+ features
348            user_allocated_ring_memory: kernel_version
349                >= &KernelVersion {
350                    major: 6,
351                    minor: 5,
352                    patch: 0,
353                },
354
355            // Kernel 6.6+ features
356            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            // Kernel 6.11+ features
382            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            // Kernel 6.12+ features
396            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    /// Check if a specific feature is available.
436    ///
437    /// # Examples
438    ///
439    /// ```rust,no_run
440    /// # use safer_ring::advanced::FeatureDetector;
441    /// let detector = FeatureDetector::new()?;
442    ///
443    /// if detector.has_feature("multi_shot") {
444    ///     println!("Multi-shot operations are supported");
445    /// }
446    /// # Ok::<(), safer_ring::error::SaferRingError>(())
447    /// ```
448    pub fn has_feature(&self, feature: &str) -> bool {
449        match feature {
450            // Legacy features
451            "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            // Kernel 6.1+ features
462            "advanced_task_work" => self.features.advanced_task_work,
463            "defer_async_work" => self.features.defer_async_work,
464
465            // Kernel 6.2+ features
466            "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            // Kernel 6.3+ features
471            "registered_ring_fd" => self.features.registered_ring_fd,
472
473            // Kernel 6.4+ features
474            "multishot_timeouts" => self.features.multishot_timeouts,
475
476            // Kernel 6.5+ features
477            "user_allocated_ring_memory" => self.features.user_allocated_ring_memory,
478
479            // Kernel 6.6+ features
480            "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            // Kernel 6.11+ features
486            "msg_ring_speedup" => self.features.msg_ring_speedup,
487            "native_bind_listen" => self.features.native_bind_listen,
488
489            // Kernel 6.12+ features
490            "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    /// Get a list of all available features.
502    ///
503    /// Returns a vector of feature names that are available on the current kernel.
504    /// This can be useful for logging, debugging, or conditional feature usage.
505    ///
506    /// # Examples
507    ///
508    /// ```rust,no_run
509    /// use safer_ring::advanced::FeatureDetector;
510    ///
511    /// let detector = FeatureDetector::new()?;
512    /// let features = detector.available_features();
513    ///
514    /// println!("Available features: {:?}", features);
515    /// for feature in features {
516    ///     println!("- {}", feature);
517    /// }
518    /// # Ok::<(), safer_ring::error::SaferRingError>(())
519    /// ```
520    pub fn available_features(&self) -> Vec<&'static str> {
521        let mut features = Vec::new();
522
523        // Legacy features
524        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        // Kernel 6.1+ features
553        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        // Kernel 6.2+ features
561        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        // Kernel 6.3+ features
572        if self.features.registered_ring_fd {
573            features.push("registered_ring_fd");
574        }
575
576        // Kernel 6.4+ features
577        if self.features.multishot_timeouts {
578            features.push("multishot_timeouts");
579        }
580
581        // Kernel 6.5+ features
582        if self.features.user_allocated_ring_memory {
583            features.push("user_allocated_ring_memory");
584        }
585
586        // Kernel 6.6+ features
587        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        // Kernel 6.11+ features
601        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        // Kernel 6.12+ features
609        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    /// Create an AdvancedConfig with features enabled based on availability.
632    ///
633    /// This method creates a configuration that balances performance and stability
634    /// by enabling safe features while keeping potentially problematic ones disabled.
635    /// SQ polling is disabled by default due to CPU usage concerns.
636    /// Some advanced features are disabled by default for stability.
637    ///
638    /// # Examples
639    ///
640    /// ```rust,no_run
641    /// use safer_ring::advanced::FeatureDetector;
642    ///
643    /// let detector = FeatureDetector::new()?;
644    /// let config = detector.create_optimal_config();
645    ///
646    /// // Use the config to create a ring with optimal settings
647    /// // let ring = Ring::with_advanced_config(config)?;
648    /// # Ok::<(), safer_ring::error::SaferRingError>(())
649    /// ```
650    pub fn create_optimal_config(&self) -> AdvancedConfig {
651        AdvancedConfig {
652            // Legacy features
653            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, // Disabled by default due to CPU usage
658            sq_thread_cpu: None,
659            coop_taskrun: self.features.coop_taskrun,
660            defer_taskrun: self.features.defer_taskrun,
661
662            // Kernel 6.1+ features
663            advanced_task_work: self.features.advanced_task_work,
664            defer_async_work: false, // Disabled by default - requires careful application integration
665
666            // Kernel 6.2+ features
667            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            // Kernel 6.3+ features
672            registered_ring_fd: self.features.registered_ring_fd,
673
674            // Kernel 6.4+ features
675            multishot_timeouts: self.features.multishot_timeouts,
676
677            // Kernel 6.5+ features
678            user_allocated_ring_memory: false, // Disabled by default - requires custom memory management
679
680            // Kernel 6.6+ features
681            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            // Kernel 6.11+ features
686            msg_ring_speedup: self.features.msg_ring_speedup,
687            native_bind_listen: self.features.native_bind_listen,
688
689            // Kernel 6.12+ features
690            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    /// Create a new feature detector with hybrid detection strategy.
700    ///
701    /// This method combines kernel version detection with direct kernel probing
702    /// for more accurate feature detection, especially useful in environments
703    /// with backported features.
704    ///
705    /// # Errors
706    ///
707    /// Returns an error if kernel version detection fails or if direct probing
708    /// cannot be initialized.
709    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            // Try to enhance feature detection with direct probing
716            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                    // Fall back to version-based detection if probing fails
728                    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    /// Merge version-based and probe-based feature detection results.
746    ///
747    /// This method combines the conservative version-based approach with
748    /// the more accurate probe-based detection, preferring probe results
749    /// when they indicate a feature is available.
750    #[cfg(target_os = "linux")]
751    fn merge_feature_detection(
752        version_features: AvailableFeatures,
753        probe_features: AvailableFeatures,
754    ) -> AvailableFeatures {
755        AvailableFeatures {
756            // Use OR logic: if either method detects a feature, consider it available
757            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            // For newer features, prefer version-based detection as primary source
768            // since opcode availability doesn't always indicate full feature support
769            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/// Direct kernel probing for io_uring features.
794///
795/// This detector uses the io_uring kernel probing mechanism to directly
796/// query the kernel for supported operations and features, providing
797/// more accurate detection than version-based approaches.
798#[cfg(target_os = "linux")]
799pub struct DirectProbeDetector {
800    probe: Probe,
801}
802
803#[cfg(target_os = "linux")]
804impl DirectProbeDetector {
805    /// Create a new direct probe detector.
806    ///
807    /// Initializes a minimal io_uring instance for probing kernel capabilities.
808    /// This requires io_uring to be available on the system and sufficient
809    /// permissions to create io_uring instances.
810    ///
811    /// # Examples
812    ///
813    /// ```rust,no_run
814    /// # #[cfg(target_os = "linux")]
815    /// # {
816    /// use safer_ring::advanced::feature_detection::DirectProbeDetector;
817    ///
818    /// match DirectProbeDetector::new() {
819    ///     Ok(detector) => {
820    ///         let features = detector.probe_kernel_features();
821    ///         println!("Direct probe completed successfully");
822    ///     }
823    ///     Err(e) => {
824    ///         eprintln!("Direct probing failed: {}", e);
825    ///         // Fall back to version-based detection
826    ///     }
827    /// }
828    /// # }
829    /// ```
830    ///
831    /// # Errors
832    ///
833    /// Returns an error if:
834    /// - io_uring initialization fails (insufficient permissions, kernel too old)
835    /// - Probe registration fails (kernel doesn't support probing)
836    /// - System resources are insufficient for creating the temporary ring
837    pub fn new() -> Result<Self> {
838        let ring = IoUring::new(1)?;
839        let mut probe = Probe::new();
840
841        // Register the probe with the kernel to get supported features
842        ring.submitter().register_probe(&mut probe)?;
843
844        // Ring is dropped here, only keeping the probe results
845        Ok(Self { probe })
846    }
847
848    /// Probe the kernel for available io_uring features.
849    ///
850    /// This method directly queries the kernel to determine which
851    /// io_uring operations and features are supported, providing
852    /// the most accurate feature detection possible.
853    ///
854    /// # Examples
855    ///
856    /// ```rust,no_run
857    /// # #[cfg(target_os = "linux")]
858    /// # {
859    /// use safer_ring::advanced::feature_detection::DirectProbeDetector;
860    ///
861    /// # fn main() -> Result<(), safer_ring::error::SaferRingError> {
862    /// let detector = DirectProbeDetector::new()?;
863    /// let features = detector.probe_kernel_features();
864    ///
865    /// if features.multi_shot {
866    ///     println!("Multi-shot operations are supported");
867    /// }
868    /// if features.buffer_selection {
869    ///     println!("Buffer selection is supported");
870    /// }
871    /// # Ok(())
872    /// # }
873    /// # }
874    /// ```
875    pub fn probe_kernel_features(&self) -> AvailableFeatures {
876        AvailableFeatures {
877            // Check for basic operation support via opcodes
878            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, // SQ polling is a setup feature, assume available if io_uring works
883            buffer_selection: self.is_opcode_supported(io_uring::opcode::Recv::CODE), // Proxy check
884            provided_buffers: self.is_opcode_supported(io_uring::opcode::Recv::CODE), // Proxy check
885            multi_shot: self.is_opcode_supported(io_uring::opcode::Accept::CODE), // Proxy for multishot support
886
887            // These are runtime/setup features that can't be directly probed via opcodes
888            // so we conservatively set them to false and rely on version detection
889            coop_taskrun: false,
890            defer_taskrun: false,
891
892            // Newer features - use conservative approach for direct probing
893            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    /// Check if a specific opcode is supported by the kernel.
917    ///
918    /// # Arguments
919    ///
920    /// * `opcode` - The operation code to check for support
921    ///
922    /// # Returns
923    ///
924    /// `true` if the opcode is supported, `false` otherwise
925    fn is_opcode_supported(&self, opcode: u8) -> bool {
926        self.probe.is_supported(opcode)
927    }
928}
929
930/// Stub implementation for non-Linux platforms.
931#[cfg(not(target_os = "linux"))]
932pub struct DirectProbeDetector;
933
934#[cfg(not(target_os = "linux"))]
935impl DirectProbeDetector {
936    /// Stub implementation for non-Linux platforms.
937    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        // Basic sanity checks
986        assert!(detector.kernel_version.major > 0 || cfg!(not(target_os = "linux")));
987
988        // Test feature querying
989        let _has_buffer_selection = detector.has_feature("buffer_selection");
990        let _available_features = detector.available_features();
991
992        // Test unknown feature
993        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        // Old kernel should not have advanced features
1006        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        // New kernel should have all features
1018        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        // Should have 6.1+ features
1034        assert!(features.advanced_task_work);
1035        assert!(features.defer_async_work);
1036
1037        // Should not have later features
1038        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        // Should have 6.1+ features
1052        assert!(features.advanced_task_work);
1053        assert!(features.defer_async_work);
1054
1055        // Should have 6.2+ features
1056        assert!(features.send_zc_reporting);
1057        assert!(features.multishot_completion_batching);
1058        assert!(features.epoll_uring_wake);
1059
1060        // Should not have later features
1061        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        // Should have all 6.6+ features
1074        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        // Should not have later features
1080        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        // Should have all latest features
1094        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        // Should also have earlier features
1102        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        // Test that all new feature strings are recognized
1112        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            // Should not panic and should return a boolean
1137            let _ = detector.has_feature(feature);
1138        }
1139
1140        // Test unknown feature
1141        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        // Should include some of the new features
1160        assert!(available.contains(&"advanced_task_work"));
1161        assert!(available.contains(&"async_cancel_op"));
1162        assert!(available.contains(&"improved_huge_pages"));
1163
1164        // Should be a significant number of features
1165        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        // Should enable safe new features
1184        assert!(config.advanced_task_work);
1185        assert!(config.async_cancel_op);
1186        assert!(config.improved_huge_pages);
1187
1188        // Should keep some features disabled by default for safety
1189        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        // This test should not panic even if direct probing fails
1197        let detector = FeatureDetector::new_with_probing().unwrap();
1198
1199        // Should have detected some kernel version
1200        assert!(detector.kernel_version.major > 0);
1201
1202        // Should have some features available
1203        let available = detector.available_features();
1204        assert!(!available.is_empty());
1205    }
1206
1207    #[test]
1208    fn test_feature_detection_consistency() {
1209        // Verify that newer kernels have all features of older kernels
1210        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            // Each newer kernel should have at least as many features as the previous
1244            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}