@@ -13,7 +13,7 @@ use log::error;
13
13
use utils:: eventfd:: EventFd ;
14
14
use utils:: u64_to_usize;
15
15
use vhost:: vhost_user:: message:: * ;
16
- use vhost:: vhost_user:: VhostUserMaster ;
16
+ use vhost:: vhost_user:: Master ;
17
17
18
18
use super :: { VhostUserBlockError , NUM_QUEUES , QUEUE_SIZE } ;
19
19
use crate :: devices:: virtio:: block_common:: CacheType ;
@@ -23,7 +23,8 @@ use crate::devices::virtio::gen::virtio_blk::{
23
23
} ;
24
24
use crate :: devices:: virtio:: gen:: virtio_ring:: VIRTIO_RING_F_EVENT_IDX ;
25
25
use crate :: devices:: virtio:: queue:: Queue ;
26
- use crate :: devices:: virtio:: vhost_user:: VhostUserHandle ;
26
+ use crate :: devices:: virtio:: vhost_user:: VhostUserHandleBackend ;
27
+ use crate :: devices:: virtio:: vhost_user:: VhostUserHandleImpl ;
27
28
use crate :: devices:: virtio:: { ActivateError , TYPE_BLOCK } ;
28
29
use crate :: logger:: { IncMetric , METRICS } ;
29
30
use crate :: vmm_config:: drive:: BlockDeviceConfig ;
@@ -32,6 +33,15 @@ use crate::vstate::memory::GuestMemoryMmap;
32
33
/// Block device config space size in bytes.
33
34
const BLOCK_CONFIG_SPACE_SIZE : u32 = 60 ;
34
35
36
+ const AVAILABLE_FEATURES : u64 = ( 1 << VIRTIO_F_VERSION_1 )
37
+ | ( 1 << VIRTIO_RING_F_EVENT_IDX )
38
+ // vhost-user specific bit. Not defined in standart virtio spec.
39
+ // Specifies ability of frontend to negotiate protocol features.
40
+ | VhostUserVirtioFeatures :: PROTOCOL_FEATURES . bits ( )
41
+ // We always try to negotiate readonly with the backend.
42
+ // If the backend is configured as readonly, we will accept it.
43
+ | ( 1 << VIRTIO_BLK_F_RO ) ;
44
+
35
45
/// Use this structure to set up the Block Device before booting the kernel.
36
46
#[ derive( Debug , PartialEq , Eq ) ]
37
47
pub struct VhostUserBlockConfig {
@@ -93,9 +103,10 @@ impl From<VhostUserBlockConfig> for BlockDeviceConfig {
93
103
}
94
104
}
95
105
106
+ pub type VhostUserBlock = VhostUserBlockImpl < Master > ;
107
+
96
108
/// vhost-user block device.
97
- #[ derive( Debug ) ]
98
- pub struct VhostUserBlock {
109
+ pub struct VhostUserBlockImpl < T : VhostUserHandleBackend > {
99
110
// Virtio fields.
100
111
pub avail_features : u64 ,
101
112
pub acked_features : u64 ,
@@ -116,28 +127,47 @@ pub struct VhostUserBlock {
116
127
pub read_only : bool ,
117
128
118
129
// Vhost user protocol handle
119
- pub vu_handle : VhostUserHandle ,
130
+ pub vu_handle : VhostUserHandleImpl < T > ,
120
131
pub vu_acked_protocol_features : u64 ,
121
132
}
122
133
123
- impl VhostUserBlock {
134
+ // Need custom implementation because otherwise `Debug` is required for `vhost::Master`
135
+ impl < T : VhostUserHandleBackend > std:: fmt:: Debug for VhostUserBlockImpl < T > {
136
+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
137
+ f. debug_struct ( "VhostUserBlockImpl" )
138
+ . field ( "avail_features" , & self . avail_features )
139
+ . field ( "acked_features" , & self . acked_features )
140
+ . field ( "config_space" , & self . config_space )
141
+ . field ( "activate_evt" , & self . activate_evt )
142
+ . field ( "queues" , & self . queues )
143
+ . field ( "queue_evts" , & self . queue_evts )
144
+ . field ( "device_state" , & self . device_state )
145
+ . field ( "irq_trigger" , & self . irq_trigger )
146
+ . field ( "id" , & self . id )
147
+ . field ( "partuuid" , & self . partuuid )
148
+ . field ( "cache_type" , & self . cache_type )
149
+ . field ( "root_device" , & self . root_device )
150
+ . field ( "read_only" , & self . read_only )
151
+ . field ( "vu_handle" , & self . vu_handle )
152
+ . field (
153
+ "vu_acked_protocol_features" ,
154
+ & self . vu_acked_protocol_features ,
155
+ )
156
+ . finish ( )
157
+ }
158
+ }
159
+
160
+ impl < T : VhostUserHandleBackend > VhostUserBlockImpl < T > {
124
161
pub fn new ( config : VhostUserBlockConfig ) -> Result < Self , VhostUserBlockError > {
125
- let mut requested_features = ( 1 << VIRTIO_F_VERSION_1 )
126
- | ( 1 << VIRTIO_RING_F_EVENT_IDX )
127
- // vhost-user specific bit. Not defined in standart virtio spec.
128
- // Specifies ability of frontend to negotiate protocol features.
129
- | VhostUserVirtioFeatures :: PROTOCOL_FEATURES . bits ( )
130
- // We always try to negotiate readonly with the backend.
131
- // If the backend is configured as readonly, we will accept it.
132
- | ( 1 << VIRTIO_BLK_F_RO ) ;
162
+ let mut requested_features = AVAILABLE_FEATURES ;
133
163
134
164
if config. cache_type == CacheType :: Writeback {
135
165
requested_features |= 1 << VIRTIO_BLK_F_FLUSH ;
136
166
}
137
167
138
168
let requested_protocol_features = VhostUserProtocolFeatures :: CONFIG ;
139
169
140
- let mut vu_handle = VhostUserHandle :: new ( & config. socket , NUM_QUEUES )
170
+ let mut vu_handle = VhostUserHandleImpl :: < T > :: new ( & config. socket , NUM_QUEUES )
141
171
. map_err ( VhostUserBlockError :: VhostUser ) ?;
142
172
let ( acked_features, acked_protocol_features) = vu_handle
143
173
. negotiate_features ( requested_features, requested_protocol_features)
@@ -303,3 +333,274 @@ impl VirtioDevice for VhostUserBlock {
303
333
false
304
334
}
305
335
}
336
+
337
+ #[ cfg( test) ]
338
+ mod tests {
339
+ #![ allow( clippy:: undocumented_unsafe_blocks) ]
340
+ #![ allow( clippy:: cast_possible_truncation) ]
341
+
342
+ use super :: * ;
343
+
344
+ use std:: os:: unix:: net:: UnixStream ;
345
+
346
+ #[ test]
347
+ fn test_new_no_features ( ) {
348
+ struct MockMaster {
349
+ sock : UnixStream ,
350
+ max_queue_num : u64 ,
351
+ is_owner : std:: cell:: UnsafeCell < bool > ,
352
+ features : u64 ,
353
+ protocol_features : VhostUserProtocolFeatures ,
354
+ hdr_flags : std:: cell:: UnsafeCell < VhostUserHeaderFlag > ,
355
+ }
356
+
357
+ impl VhostUserHandleBackend for MockMaster {
358
+ fn from_stream ( sock : UnixStream , max_queue_num : u64 ) -> Self {
359
+ Self {
360
+ sock,
361
+ max_queue_num,
362
+ is_owner : std:: cell:: UnsafeCell :: new ( false ) ,
363
+ features : 0 ,
364
+ protocol_features : VhostUserProtocolFeatures :: empty ( ) ,
365
+ hdr_flags : std:: cell:: UnsafeCell :: new ( VhostUserHeaderFlag :: empty ( ) ) ,
366
+ }
367
+ }
368
+
369
+ fn set_owner ( & self ) -> Result < ( ) , vhost:: Error > {
370
+ unsafe { * self . is_owner . get ( ) = true } ;
371
+ Ok ( ( ) )
372
+ }
373
+
374
+ fn set_hdr_flags ( & self , flags : VhostUserHeaderFlag ) {
375
+ unsafe { * self . hdr_flags . get ( ) = flags } ;
376
+ }
377
+
378
+ fn get_features ( & self ) -> Result < u64 , vhost:: Error > {
379
+ Ok ( self . features )
380
+ }
381
+
382
+ fn get_protocol_features ( & mut self ) -> Result < VhostUserProtocolFeatures , vhost:: Error > {
383
+ Ok ( self . protocol_features )
384
+ }
385
+
386
+ fn set_protocol_features (
387
+ & mut self ,
388
+ features : VhostUserProtocolFeatures ,
389
+ ) -> Result < ( ) , vhost:: Error > {
390
+ self . protocol_features = features;
391
+ Ok ( ( ) )
392
+ }
393
+ }
394
+
395
+ let tmp_dir = utils:: tempdir:: TempDir :: new ( ) . unwrap ( ) ;
396
+ let tmp_dir_path_str = tmp_dir. as_path ( ) . to_str ( ) . unwrap ( ) ;
397
+ let tmp_socket_path = format ! ( "{tmp_dir_path_str}/tmp_socket" ) ;
398
+
399
+ unsafe {
400
+ let socketfd = libc:: socket ( libc:: AF_UNIX , libc:: SOCK_STREAM , 0 ) ;
401
+ if socketfd < 0 {
402
+ panic ! ( "Cannot create socket" ) ;
403
+ }
404
+ let mut socket_addr = libc:: sockaddr_un {
405
+ sun_family : libc:: AF_UNIX as u16 ,
406
+ sun_path : [ 0 ; 108 ] ,
407
+ } ;
408
+
409
+ std:: ptr:: copy :: < i8 > (
410
+ tmp_socket_path. as_ptr ( ) . cast ( ) ,
411
+ socket_addr. sun_path . as_mut_ptr ( ) ,
412
+ tmp_socket_path. as_bytes ( ) . len ( ) ,
413
+ ) ;
414
+
415
+ let bind = libc:: bind (
416
+ socketfd,
417
+ ( & socket_addr as * const libc:: sockaddr_un ) . cast ( ) ,
418
+ std:: mem:: size_of :: < libc:: sockaddr_un > ( ) as u32 ,
419
+ ) ;
420
+ if bind < 0 {
421
+ panic ! ( "Cannot bind socket" ) ;
422
+ }
423
+
424
+ let listen = libc:: listen ( socketfd, 1 ) ;
425
+ if listen < 0 {
426
+ panic ! ( "Cannot listen on socket" ) ;
427
+ }
428
+ }
429
+
430
+ let vhost_block_config = VhostUserBlockConfig {
431
+ drive_id : "test_drive" . to_string ( ) ,
432
+ partuuid : None ,
433
+ is_root_device : false ,
434
+ cache_type : CacheType :: Unsafe ,
435
+ socket : tmp_socket_path. clone ( ) ,
436
+ } ;
437
+ let vhost_block = VhostUserBlockImpl :: < MockMaster > :: new ( vhost_block_config) . unwrap ( ) ;
438
+
439
+ assert_eq ! (
440
+ vhost_block
441
+ . vu_handle
442
+ . vu
443
+ . sock
444
+ . peer_addr( )
445
+ . unwrap( )
446
+ . as_pathname( )
447
+ . unwrap( )
448
+ . to_str( )
449
+ . unwrap( ) ,
450
+ & tmp_socket_path,
451
+ ) ;
452
+ assert_eq ! ( vhost_block. vu_handle. vu. max_queue_num, NUM_QUEUES ) ;
453
+ assert ! ( unsafe { * vhost_block. vu_handle. vu. is_owner. get( ) } ) ;
454
+ assert_eq ! ( vhost_block. avail_features, 0 ) ;
455
+ assert_eq ! ( vhost_block. acked_features, 0 ) ;
456
+ assert_eq ! ( vhost_block. vu_acked_protocol_features, 0 ) ;
457
+ assert_eq ! (
458
+ unsafe { * vhost_block. vu_handle. vu. hdr_flags. get( ) } ,
459
+ VhostUserHeaderFlag :: empty( )
460
+ ) ;
461
+ assert ! ( !vhost_block. root_device) ;
462
+ assert ! ( !vhost_block. read_only) ;
463
+ assert_eq ! ( vhost_block. config_space, Vec :: <u8 >:: new( ) ) ;
464
+ }
465
+
466
+ #[ test]
467
+ fn test_new_all_features ( ) {
468
+ struct MockMaster {
469
+ sock : UnixStream ,
470
+ max_queue_num : u64 ,
471
+ is_owner : std:: cell:: UnsafeCell < bool > ,
472
+ features : u64 ,
473
+ protocol_features : VhostUserProtocolFeatures ,
474
+ hdr_flags : std:: cell:: UnsafeCell < VhostUserHeaderFlag > ,
475
+ }
476
+
477
+ impl VhostUserHandleBackend for MockMaster {
478
+ fn from_stream ( sock : UnixStream , max_queue_num : u64 ) -> Self {
479
+ Self {
480
+ sock,
481
+ max_queue_num,
482
+ is_owner : std:: cell:: UnsafeCell :: new ( false ) ,
483
+ features : AVAILABLE_FEATURES | ( 1 << VIRTIO_BLK_F_FLUSH ) ,
484
+
485
+ protocol_features : VhostUserProtocolFeatures :: all ( ) ,
486
+ hdr_flags : std:: cell:: UnsafeCell :: new ( VhostUserHeaderFlag :: empty ( ) ) ,
487
+ }
488
+ }
489
+
490
+ fn set_owner ( & self ) -> Result < ( ) , vhost:: Error > {
491
+ unsafe { * self . is_owner . get ( ) = true } ;
492
+ Ok ( ( ) )
493
+ }
494
+
495
+ fn set_hdr_flags ( & self , flags : VhostUserHeaderFlag ) {
496
+ unsafe { * self . hdr_flags . get ( ) = flags } ;
497
+ }
498
+
499
+ fn get_features ( & self ) -> Result < u64 , vhost:: Error > {
500
+ Ok ( self . features )
501
+ }
502
+
503
+ fn get_protocol_features ( & mut self ) -> Result < VhostUserProtocolFeatures , vhost:: Error > {
504
+ Ok ( self . protocol_features )
505
+ }
506
+
507
+ fn set_protocol_features (
508
+ & mut self ,
509
+ features : VhostUserProtocolFeatures ,
510
+ ) -> Result < ( ) , vhost:: Error > {
511
+ self . protocol_features = features;
512
+ Ok ( ( ) )
513
+ }
514
+
515
+ fn get_config (
516
+ & mut self ,
517
+ _offset : u32 ,
518
+ _size : u32 ,
519
+ _flags : VhostUserConfigFlags ,
520
+ _buf : & [ u8 ] ,
521
+ ) -> Result < ( VhostUserConfig , VhostUserConfigPayload ) , vhost:: Error > {
522
+ Ok ( ( VhostUserConfig :: default ( ) , vec ! [ 0x69 , 0x69 , 0x69 ] ) )
523
+ }
524
+ }
525
+
526
+ let tmp_dir = utils:: tempdir:: TempDir :: new ( ) . unwrap ( ) ;
527
+ let tmp_dir_path_str = tmp_dir. as_path ( ) . to_str ( ) . unwrap ( ) ;
528
+ let tmp_socket_path = format ! ( "{tmp_dir_path_str}/tmp_socket" ) ;
529
+
530
+ unsafe {
531
+ let socketfd = libc:: socket ( libc:: AF_UNIX , libc:: SOCK_STREAM , 0 ) ;
532
+ if socketfd < 0 {
533
+ panic ! ( "Cannot create socket" ) ;
534
+ }
535
+ let mut socket_addr = libc:: sockaddr_un {
536
+ sun_family : libc:: AF_UNIX as u16 ,
537
+ sun_path : [ 0 ; 108 ] ,
538
+ } ;
539
+
540
+ std:: ptr:: copy :: < i8 > (
541
+ tmp_socket_path. as_ptr ( ) . cast ( ) ,
542
+ socket_addr. sun_path . as_mut_ptr ( ) ,
543
+ tmp_socket_path. as_bytes ( ) . len ( ) ,
544
+ ) ;
545
+
546
+ let bind = libc:: bind (
547
+ socketfd,
548
+ ( & socket_addr as * const libc:: sockaddr_un ) . cast ( ) ,
549
+ std:: mem:: size_of :: < libc:: sockaddr_un > ( ) as u32 ,
550
+ ) ;
551
+ if bind < 0 {
552
+ panic ! ( "Cannot bind socket" ) ;
553
+ }
554
+
555
+ let listen = libc:: listen ( socketfd, 1 ) ;
556
+ if listen < 0 {
557
+ panic ! ( "Cannot listen on socket" ) ;
558
+ }
559
+ }
560
+
561
+ let vhost_block_config = VhostUserBlockConfig {
562
+ drive_id : "test_drive" . to_string ( ) ,
563
+ partuuid : None ,
564
+ is_root_device : false ,
565
+ cache_type : CacheType :: Writeback ,
566
+ socket : tmp_socket_path. clone ( ) ,
567
+ } ;
568
+ let vhost_block = VhostUserBlockImpl :: < MockMaster > :: new ( vhost_block_config) . unwrap ( ) ;
569
+
570
+ assert_eq ! (
571
+ vhost_block
572
+ . vu_handle
573
+ . vu
574
+ . sock
575
+ . peer_addr( )
576
+ . unwrap( )
577
+ . as_pathname( )
578
+ . unwrap( )
579
+ . to_str( )
580
+ . unwrap( ) ,
581
+ & tmp_socket_path,
582
+ ) ;
583
+ assert_eq ! ( vhost_block. vu_handle. vu. max_queue_num, NUM_QUEUES ) ;
584
+ assert ! ( unsafe { * vhost_block. vu_handle. vu. is_owner. get( ) } ) ;
585
+
586
+ assert_eq ! (
587
+ vhost_block. avail_features,
588
+ AVAILABLE_FEATURES | ( 1 << VIRTIO_BLK_F_FLUSH )
589
+ ) ;
590
+ assert_eq ! (
591
+ vhost_block. acked_features,
592
+ VhostUserVirtioFeatures :: PROTOCOL_FEATURES . bits( )
593
+ ) ;
594
+ assert_eq ! (
595
+ vhost_block. vu_acked_protocol_features,
596
+ VhostUserProtocolFeatures :: CONFIG . bits( )
597
+ ) ;
598
+ assert_eq ! (
599
+ unsafe { * vhost_block. vu_handle. vu. hdr_flags. get( ) } ,
600
+ VhostUserHeaderFlag :: empty( )
601
+ ) ;
602
+ assert ! ( !vhost_block. root_device) ;
603
+ assert ! ( !vhost_block. read_only) ;
604
+ assert_eq ! ( vhost_block. config_space, vec![ 0x69 , 0x69 , 0x69 ] ) ;
605
+ }
606
+ }
0 commit comments