11use core:: f64;
2- use std:: { convert:: TryFrom , fmt:: Debug , os:: raw:: c_void, pin:: Pin , ptr:: NonNull } ;
2+ use std:: { convert:: TryFrom , fmt:: Debug , os:: raw:: c_void, pin:: Pin , ptr:: NonNull , str :: FromStr } ;
33
44use byte_unit:: Byte ;
55use events_api:: event:: EventAction ;
@@ -210,9 +210,9 @@ impl Lvs {
210210 }
211211
212212 /// Size upto which blobstore can be expanded.
213- pub fn max_expandable_size ( & self ) -> u64 {
213+ pub fn max_expandable_size ( & self ) -> Option < u64 > {
214214 // TODO: Use spdk function when changes gets merged.
215- 0
215+ Some ( 0 )
216216 }
217217
218218 /// returns the UUID of the lvs
@@ -422,21 +422,37 @@ impl Lvs {
422422 }
423423 }
424424
425- /// Converts floating point metadata reservation ratio into SPDK's format.
426- fn mdp_ratio ( args : & PoolArgs ) -> Result < u32 , LvsError > {
427- if let Some ( mut h) = args. md_args . as_ref ( ) . and_then ( |p| p. max_expansion . clone ( ) ) {
428- if h. ends_with ( "x" ) {
429- let _ = h. pop ( ) ;
430- let factor_parsed = h
431- . parse :: < f64 > ( )
432- . map_err ( |_| LvsError :: MaxExpansionFactorParse { factor : h } ) ?;
433- Ok ( ( factor_parsed * 100.0 ) as u32 )
434- } else {
435- Err ( LvsError :: MaxExpansionFactorFormat { factor : h } )
436- }
425+ /// Derive num_md_pages_per_cluster_ratio from max_expansion which can be a factor or absolute size.
426+ fn md_resv_ratio ( args : & PoolArgs , capacity : u64 ) -> Result < u32 , LvsError > {
427+ let param = match args. md_args . as_ref ( ) . and_then ( |p| p. max_expansion . clone ( ) ) {
428+ Some ( p) => p,
429+ None => return Ok ( 100 ) ,
430+ } ;
431+ let factor = if let Some ( stripped) = param. strip_suffix ( 'x' ) {
432+ stripped
433+ . parse :: < f64 > ( )
434+ . map_err ( |error| LvsError :: MaxExpansionParse {
435+ msg : format ! (
436+ "Failed to parse factor max_expansion {stripped} as float: {error}"
437+ ) ,
438+ } ) ?
439+ } else if param. ends_with ( 'B' ) {
440+ let expand_bytes =
441+ Byte :: from_str ( & param) . map_err ( |error| LvsError :: MaxExpansionParse {
442+ msg : format ! ( "Failed to parse max_expansion {param} as bytes: {error}" ) ,
443+ } ) ?;
444+ expand_bytes. as_u64 ( ) as f64 / capacity as f64
437445 } else {
438- Ok ( 0 )
439- }
446+ return Err ( LvsError :: MaxExpansionParse {
447+ msg : format ! ( "Max expansion factor {param} does not end with x or B" ) ,
448+ } ) ;
449+ } ;
450+ // The Blobstore ensures that we have enough pages in used_cluster_mask to track the current device size.
451+ // So, If maxExpansion results is < 1x it still shouldn't matter. Its same as having
452+ // default reservation. However, it does impact how many md pages per cluster are reserved.
453+ // For ex. if the factor turns out to be 0.5 then blobstore reserves 1 page per 2 clusters.
454+ // So lets ensures that we pass at least default reservation.
455+ Ok ( ( factor. max ( 1.0 ) * 100.0 ) as u32 )
440456 }
441457
442458 /// Creates a pool on base bdev.
@@ -450,7 +466,7 @@ impl Lvs {
450466 let bdev_name = if let Some ( ref c) = args. crypto_vbdev_name {
451467 c. clone ( ) . into_cstring ( )
452468 } else {
453- bdev. into_cstring ( )
469+ bdev. clone ( ) . into_cstring ( )
454470 } ;
455471
456472 let cluster_size = if let Some ( cluster_size) = args. cluster_size {
@@ -474,9 +490,14 @@ impl Lvs {
474490 msg : format ! ( "{cluster_size}, larger than max limit {MAX_CLUSTER_SIZE}" ) ,
475491 } ) ;
476492 }
477-
478- let mdp_ratio = Self :: mdp_ratio ( & args) ?;
479-
493+ let bdev = UntypedBdev :: lookup_by_name ( & bdev) . ok_or ( LvsError :: InvalidBdev {
494+ source : BdevError :: BdevNotFound {
495+ name : bdev. to_string ( ) ,
496+ } ,
497+ name : bdev. to_string ( ) ,
498+ } ) ?;
499+ let bdev_capacity = bdev. num_blocks ( ) * bdev. block_len ( ) as u64 ;
500+ let mdp_ratio = Self :: md_resv_ratio ( & args, bdev_capacity) ?;
480501 let ( sender, receiver) = pair :: < ErrnoResult < Lvs > > ( ) ;
481502 unsafe {
482503 if let Some ( uuid) = & args. uuid {
0 commit comments