@@ -50,9 +50,48 @@ impl UblkQueueAffinity {
5050 pub fn addr ( & self ) -> * const u8 {
5151 self . affinity . as_bytes ( ) . as_ptr ( )
5252 }
53+
5354 pub fn to_bits_vec ( & self ) -> Vec < usize > {
5455 self . affinity . into_iter ( ) . collect ( )
5556 }
57+
58+ /// Get a random CPU from the affinity set
59+ fn get_random_cpu ( & self ) -> Option < usize > {
60+ let cpus: Vec < usize > = self . affinity . into_iter ( ) . collect ( ) ;
61+ if cpus. is_empty ( ) {
62+ return None ;
63+ }
64+
65+ // Simple pseudo-random selection using current time and thread ID
66+ let mut seed = std:: time:: SystemTime :: now ( )
67+ . duration_since ( std:: time:: UNIX_EPOCH )
68+ . unwrap_or_default ( )
69+ . as_nanos ( ) as usize ;
70+
71+ unsafe {
72+ seed = seed. wrapping_add ( libc:: gettid ( ) as usize ) ;
73+ }
74+
75+ Some ( cpus[ seed % cpus. len ( ) ] )
76+ }
77+
78+ /// Create a new affinity with only the specified CPU
79+ pub fn from_single_cpu ( cpu : usize ) -> UblkQueueAffinity {
80+ let mut affinity = UblkQueueAffinity :: new ( ) ;
81+ affinity. affinity . set ( cpu, true ) ;
82+ affinity
83+ }
84+
85+ /// Set a specific CPU in the affinity
86+ pub fn set_cpu ( & mut self , cpu : usize ) {
87+ self . affinity . set ( cpu, true ) ;
88+ }
89+
90+ /// Clear all CPUs and set only the specified one
91+ pub fn set_only_cpu ( & mut self , cpu : usize ) {
92+ self . affinity = Bitmap :: new ( ) ;
93+ self . affinity . set ( cpu, true ) ;
94+ }
5695}
5796
5897#[ repr( C ) ]
@@ -265,6 +304,7 @@ struct UblkCtrlInner {
265304 dev_flags : UblkFlags ,
266305 cmd_token : i32 ,
267306 queue_tids : Vec < i32 > ,
307+ queue_selected_cpus : Vec < usize > ,
268308 nr_queues_configured : u16 ,
269309}
270310
@@ -322,6 +362,13 @@ impl UblkCtrlInner {
322362 }
323363 tids
324364 } ,
365+ queue_selected_cpus : {
366+ let mut cpus = Vec :: < usize > :: with_capacity ( nr_queues as usize ) ;
367+ unsafe {
368+ cpus. set_len ( nr_queues as usize ) ;
369+ }
370+ cpus
371+ } ,
325372 nr_queues_configured : 0 ,
326373 dev_flags,
327374 features : None ,
@@ -898,8 +945,16 @@ impl UblkCtrlInner {
898945 let mut map: serde_json:: Map < String , serde_json:: Value > = serde_json:: Map :: new ( ) ;
899946
900947 for qid in 0 ..dev. dev_info . nr_hw_queues {
901- let mut affinity = self :: UblkQueueAffinity :: new ( ) ;
902- self . get_queue_affinity ( qid as u32 , & mut affinity) ?;
948+ let affinity = if self . dev_flags . contains ( UblkFlags :: UBLK_DEV_F_SINGLE_CPU_AFFINITY ) {
949+ // Use the stored single CPU affinity
950+ let selected_cpu = self . queue_selected_cpus [ qid as usize ] ;
951+ UblkQueueAffinity :: from_single_cpu ( selected_cpu)
952+ } else {
953+ // Use the original full affinity from the kernel
954+ let mut full_affinity = UblkQueueAffinity :: new ( ) ;
955+ self . get_queue_affinity ( qid as u32 , & mut full_affinity) ?;
956+ full_affinity
957+ } ;
903958
904959 map. insert (
905960 format ! ( "{}" , qid) ,
@@ -1410,6 +1465,23 @@ impl UblkCtrl {
14101465
14111466 let mut affinity = UblkQueueAffinity :: new ( ) ;
14121467 self . get_queue_affinity ( q as u32 , & mut affinity) . unwrap ( ) ;
1468+
1469+ let ( final_affinity, selected_cpu) = if self . get_dev_flags ( ) . contains ( UblkFlags :: UBLK_DEV_F_SINGLE_CPU_AFFINITY ) {
1470+ // Select a random CPU from the affinity and create single-CPU affinity
1471+ let selected_cpu = affinity. get_random_cpu ( ) . unwrap_or ( 0 ) ;
1472+ let single_cpu_affinity = UblkQueueAffinity :: from_single_cpu ( selected_cpu) ;
1473+ ( single_cpu_affinity, Some ( selected_cpu) )
1474+ } else {
1475+ // Use original full affinity
1476+ ( affinity, None )
1477+ } ;
1478+
1479+ // Store the selected CPU for later use in build_json (if single CPU mode is enabled)
1480+ if let Some ( cpu) = selected_cpu {
1481+ let mut inner = self . get_inner_mut ( ) ;
1482+ inner. queue_selected_cpus [ q as usize ] = cpu;
1483+ }
1484+
14131485 let mut _q_fn = q_fn. clone ( ) ;
14141486
14151487 q_threads. push ( std:: thread:: spawn ( move || {
@@ -1418,8 +1490,8 @@ impl UblkCtrl {
14181490 unsafe {
14191491 libc:: pthread_setaffinity_np (
14201492 libc:: pthread_self ( ) ,
1421- affinity . buf_len ( ) ,
1422- affinity . addr ( ) as * const libc:: cpu_set_t ,
1493+ final_affinity . buf_len ( ) ,
1494+ final_affinity . addr ( ) as * const libc:: cpu_set_t ,
14231495 ) ;
14241496 }
14251497 _tx. send ( ( q, unsafe { libc:: gettid ( ) } ) ) . unwrap ( ) ;
@@ -1510,7 +1582,7 @@ impl UblkCtrl {
15101582
15111583#[ cfg( test) ]
15121584mod tests {
1513- use crate :: ctrl:: UblkCtrlBuilder ;
1585+ use crate :: ctrl:: { UblkCtrlBuilder , UblkQueueAffinity } ;
15141586 use crate :: io:: { UblkDev , UblkIOCtx , UblkQueue } ;
15151587 use crate :: UblkError ;
15161588 use crate :: { ctrl:: UblkCtrl , UblkFlags , UblkIORes } ;
@@ -1718,4 +1790,60 @@ mod tests {
17181790
17191791 handle. join ( ) . unwrap ( ) ;
17201792 }
1793+
1794+ /// Test UBLK_DEV_F_SINGLE_CPU_AFFINITY feature
1795+ #[ test]
1796+ fn test_single_cpu_affinity ( ) {
1797+ // Test 1: Verify the flag is properly defined and can be used
1798+ let single_cpu_flags = UblkFlags :: UBLK_DEV_F_ADD_DEV | UblkFlags :: UBLK_DEV_F_SINGLE_CPU_AFFINITY ;
1799+ let normal_flags = UblkFlags :: UBLK_DEV_F_ADD_DEV ;
1800+
1801+ assert ! ( single_cpu_flags. contains( UblkFlags :: UBLK_DEV_F_SINGLE_CPU_AFFINITY ) ) ;
1802+ assert ! ( !normal_flags. contains( UblkFlags :: UBLK_DEV_F_SINGLE_CPU_AFFINITY ) ) ;
1803+
1804+ // Test 2: Create control devices with and without the flag
1805+ let ctrl_with_flag = UblkCtrlBuilder :: default ( )
1806+ . name ( "test_single_cpu" )
1807+ . depth ( 16_u16 )
1808+ . nr_queues ( 2_u16 )
1809+ . dev_flags ( single_cpu_flags)
1810+ . build ( )
1811+ . unwrap ( ) ;
1812+
1813+ let ctrl_without_flag = UblkCtrlBuilder :: default ( )
1814+ . name ( "test_normal" )
1815+ . depth ( 16_u16 )
1816+ . nr_queues ( 2_u16 )
1817+ . dev_flags ( normal_flags)
1818+ . build ( )
1819+ . unwrap ( ) ;
1820+
1821+ // Test 3: Verify flag is stored correctly in the control device
1822+ assert ! ( ctrl_with_flag. get_dev_flags( ) . contains( UblkFlags :: UBLK_DEV_F_SINGLE_CPU_AFFINITY ) ) ;
1823+ assert ! ( !ctrl_without_flag. get_dev_flags( ) . contains( UblkFlags :: UBLK_DEV_F_SINGLE_CPU_AFFINITY ) ) ;
1824+
1825+ // Test 4: Test UblkQueueAffinity helper methods
1826+ let test_affinity = UblkQueueAffinity :: from_single_cpu ( 3 ) ;
1827+ let bits = test_affinity. to_bits_vec ( ) ;
1828+ assert_eq ! ( bits. len( ) , 1 , "Single CPU affinity should contain exactly one CPU" ) ;
1829+ assert_eq ! ( bits[ 0 ] , 3 , "Single CPU affinity should contain CPU 3" ) ;
1830+
1831+ // Test 5: Test random CPU selection (create an affinity with multiple CPUs and verify selection)
1832+ let mut multi_cpu_affinity = UblkQueueAffinity :: new ( ) ;
1833+ multi_cpu_affinity. set_cpu ( 1 ) ;
1834+ multi_cpu_affinity. set_cpu ( 3 ) ;
1835+ multi_cpu_affinity. set_cpu ( 5 ) ;
1836+
1837+ let selected_cpu = multi_cpu_affinity. get_random_cpu ( ) ;
1838+ assert ! ( selected_cpu. is_some( ) , "Should be able to select a CPU from multi-CPU affinity" ) ;
1839+
1840+ let cpu = selected_cpu. unwrap ( ) ;
1841+ assert ! ( cpu == 1 || cpu == 3 || cpu == 5 , "Selected CPU should be one of the available CPUs (1, 3, or 5), got {}" , cpu) ;
1842+
1843+ println ! ( "✓ Single CPU affinity feature tests passed" ) ;
1844+ println ! ( " - Flag definition and usage: PASS" ) ;
1845+ println ! ( " - Control device flag storage: PASS" ) ;
1846+ println ! ( " - Single CPU affinity creation: PASS" ) ;
1847+ println ! ( " - Random CPU selection: PASS (selected CPU {})" , cpu) ;
1848+ }
17211849}
0 commit comments