@ -87,8 +87,10 @@ static BlockDriverState *bdrv_open_inherit(const char *filename,
static bool bdrv_recurse_has_child ( BlockDriverState * bs ,
BlockDriverState * child ) ;
static void bdrv_child_free ( BdrvChild * child ) ;
static void bdrv_replace_child_noperm ( BdrvChild * * child ,
BlockDriverState * new_bs ) ;
BlockDriverState * new_bs ,
bool free_empty_child ) ;
static void bdrv_remove_file_or_backing_child ( BlockDriverState * bs ,
BdrvChild * child ,
Transaction * tran ) ;
@ -2256,12 +2258,16 @@ typedef struct BdrvReplaceChildState {
BdrvChild * child ;
BdrvChild * * childp ;
BlockDriverState * old_bs ;
bool free_empty_child ;
} BdrvReplaceChildState ;
static void bdrv_replace_child_commit ( void * opaque )
{
BdrvReplaceChildState * s = opaque ;
if ( s - > free_empty_child & & ! s - > child - > bs ) {
bdrv_child_free ( s - > child ) ;
}
bdrv_unref ( s - > old_bs ) ;
}
@ -2278,22 +2284,26 @@ static void bdrv_replace_child_abort(void *opaque)
* modify the BdrvChild * pointer we indirectly pass to it , i . e . it
* will not modify s - > child . From that perspective , it does not matter
* whether we pass s - > childp or & s - > child .
* ( TODO : Right now , bdrv_replace_child_noperm ( ) never modifies that
* pointer anyway ( though it will in the future ) , so at this point it
* absolutely does not matter whether we pass s - > childp or & s - > child . )
* ( 2 ) If new_bs is not NULL , s - > childp will be NULL . We then cannot use
* it here .
* ( 3 ) If new_bs is NULL , * s - > childp will have been NULLed by
* bdrv_replace_child_tran ( ) ' s bdrv_replace_child_noperm ( ) call , and we
* must not pass a NULL * s - > childp here .
* ( TODO : In its current state , bdrv_replace_child_noperm ( ) will not
* have NULLed * s - > childp , so this does not apply yet . It will in the
* future . )
*
* So whether new_bs was NULL or not , we cannot pass s - > childp here ; and in
* any case , there is no reason to pass it anyway .
*/
bdrv_replace_child_noperm ( & s - > child , s - > old_bs ) ;
bdrv_replace_child_noperm ( & s - > child , s - > old_bs , true ) ;
/*
* The child was pre - existing , so s - > old_bs must be non - NULL , and
* s - > child thus must not have been freed
*/
assert ( s - > child ! = NULL ) ;
if ( ! new_bs ) {
/* As described above, *s->childp was cleared, so restore it */
assert ( s - > childp ! = NULL ) ;
* s - > childp = s - > child ;
}
bdrv_unref ( new_bs ) ;
}
@ -2310,30 +2320,44 @@ static TransactionActionDrv bdrv_replace_child_drv = {
*
* The function doesn ' t update permissions , caller is responsible for this .
*
* ( * childp ) - > bs must not be NULL .
*
* Note that if new_bs = = NULL , @ childp is stored in a state object attached
* to @ tran , so that the old child can be reinstated in the abort handler .
* Therefore , if @ new_bs can be NULL , @ childp must stay valid until the
* transaction is committed or aborted .
*
* ( TODO : The reinstating does not happen yet , but it will once
* bdrv_replace_child_noperm ( ) NULLs * childp when new_bs is NULL . )
* If @ free_empty_child is true and @ new_bs is NULL , the BdrvChild is
* freed ( on commit ) . @ free_empty_child should only be false if the
* caller will free the BDrvChild themselves ( which may be important
* if this is in turn called in another transactional context ) .
*/
static void bdrv_replace_child_tran ( BdrvChild * * childp ,
BlockDriverState * new_bs ,
Transaction * tran )
Transaction * tran ,
bool free_empty_child )
{
BdrvReplaceChildState * s = g_new ( BdrvReplaceChildState , 1 ) ;
* s = ( BdrvReplaceChildState ) {
. child = * childp ,
. childp = new_bs = = NULL ? childp : NULL ,
. old_bs = ( * childp ) - > bs ,
. free_empty_child = free_empty_child ,
} ;
tran_add ( tran , & bdrv_replace_child_drv , s ) ;
/* The abort handler relies on this */
assert ( s - > old_bs ! = NULL ) ;
if ( new_bs ) {
bdrv_ref ( new_bs ) ;
}
bdrv_replace_child_noperm ( childp , new_bs ) ;
/*
* Pass free_empty_child = false , we will free the child ( if
* necessary ) in bdrv_replace_child_commit ( ) ( if our
* @ free_empty_child parameter was true ) .
*/
bdrv_replace_child_noperm ( childp , new_bs , false ) ;
/* old_bs reference is transparently moved from *childp to @s */
}
@ -2705,8 +2729,22 @@ uint64_t bdrv_qapi_perm_to_blk_perm(BlockPermission qapi_perm)
return permissions [ qapi_perm ] ;
}
/**
* Replace ( * childp ) - > bs by @ new_bs .
*
* If @ new_bs is NULL , * childp will be set to NULL , too : BDS parents
* generally cannot handle a BdrvChild with . bs = = NULL , so clearing
* BdrvChild . bs should generally immediately be followed by the
* BdrvChild pointer being cleared as well .
*
* If @ free_empty_child is true and @ new_bs is NULL , the BdrvChild is
* freed . @ free_empty_child should only be false if the caller will
* free the BdrvChild themselves ( this may be important in a
* transactional context , where it may only be freed on commit ) .
*/
static void bdrv_replace_child_noperm ( BdrvChild * * childp ,
BlockDriverState * new_bs )
BlockDriverState * new_bs ,
bool free_empty_child )
{
BdrvChild * child = * childp ;
BlockDriverState * old_bs = child - > bs ;
@ -2743,6 +2781,9 @@ static void bdrv_replace_child_noperm(BdrvChild **childp,
}
child - > bs = new_bs ;
if ( ! new_bs ) {
* childp = NULL ;
}
if ( new_bs ) {
QLIST_INSERT_HEAD ( & new_bs - > parents , child , next_parent ) ;
@ -2772,6 +2813,10 @@ static void bdrv_replace_child_noperm(BdrvChild **childp,
bdrv_parent_drained_end_single ( child ) ;
drain_saldo + + ;
}
if ( free_empty_child & & ! child - > bs ) {
bdrv_child_free ( child ) ;
}
}
/**
@ -2801,7 +2846,14 @@ static void bdrv_attach_child_common_abort(void *opaque)
BdrvChild * child = * s - > child ;
BlockDriverState * bs = child - > bs ;
bdrv_replace_child_noperm ( s - > child , NULL ) ;
/*
* Pass free_empty_child = false , because we still need the child
* for the AioContext operations on the parent below ; those
* BdrvChildClass methods all work on a BdrvChild object , so we
* need to keep it as an empty shell ( after this function , it will
* not be attached to any parent , and it will not have a . bs ) .
*/
bdrv_replace_child_noperm ( s - > child , NULL , false ) ;
if ( bdrv_get_aio_context ( bs ) ! = s - > old_child_ctx ) {
bdrv_try_set_aio_context ( bs , s - > old_child_ctx , & error_abort ) ;
@ -2823,7 +2875,6 @@ static void bdrv_attach_child_common_abort(void *opaque)
bdrv_unref ( bs ) ;
bdrv_child_free ( child ) ;
* s - > child = NULL ;
}
static TransactionActionDrv bdrv_attach_child_common_drv = {
@ -2901,7 +2952,9 @@ static int bdrv_attach_child_common(BlockDriverState *child_bs,
}
bdrv_ref ( child_bs ) ;
bdrv_replace_child_noperm ( & new_child , child_bs ) ;
bdrv_replace_child_noperm ( & new_child , child_bs , true ) ;
/* child_bs was non-NULL, so new_child must not have been freed */
assert ( new_child ! = NULL ) ;
* child = new_child ;
@ -2960,8 +3013,7 @@ static void bdrv_detach_child(BdrvChild **childp)
{
BlockDriverState * old_bs = ( * childp ) - > bs ;
bdrv_replace_child_noperm ( childp , NULL ) ;
bdrv_child_free ( * childp ) ;
bdrv_replace_child_noperm ( childp , NULL , true ) ;
if ( old_bs ) {
/*
@ -4951,7 +5003,11 @@ static void bdrv_remove_file_or_backing_child(BlockDriverState *bs,
}
if ( child - > bs ) {
bdrv_replace_child_tran ( childp , NULL , tran ) ;
/*
* Pass free_empty_child = false , we will free the child in
* bdrv_remove_filter_or_cow_child_commit ( )
*/
bdrv_replace_child_tran ( childp , NULL , tran , false ) ;
}
s = g_new ( BdrvRemoveFilterOrCowChild , 1 ) ;
@ -4961,8 +5017,6 @@ static void bdrv_remove_file_or_backing_child(BlockDriverState *bs,
. is_backing = ( childp = = & bs - > backing ) ,
} ;
tran_add ( tran , & bdrv_remove_filter_or_cow_child_drv , s ) ;
* childp = NULL ;
}
/*
@ -5005,7 +5059,7 @@ static int bdrv_replace_node_noperm(BlockDriverState *from,
* Passing a pointer to the local variable @ c is fine here , because
* @ to is not NULL , and so & c will not be attached to the transaction .
*/
bdrv_replace_child_tran ( & c , to , tran ) ;
bdrv_replace_child_tran ( & c , to , tran , true ) ;
}
return 0 ;
@ -5161,7 +5215,9 @@ int bdrv_replace_child_bs(BdrvChild *child, BlockDriverState *new_bs,
bdrv_drained_begin ( old_bs ) ;
bdrv_drained_begin ( new_bs ) ;
bdrv_replace_child_tran ( & child , new_bs , tran ) ;
bdrv_replace_child_tran ( & child , new_bs , tran , true ) ;
/* @new_bs must have been non-NULL, so @child must not have been freed */
assert ( child ! = NULL ) ;
found = g_hash_table_new ( NULL , NULL ) ;
refresh_list = bdrv_topological_dfs ( refresh_list , found , old_bs ) ;