Skip to content

Commit 65ec25e

Browse files
committed
CP-53555: Split unplug atomic into deactivate/detach
Signed-off-by: Steven Woods <[email protected]>
1 parent 760ad59 commit 65ec25e

6 files changed

+241
-2
lines changed

Diff for: ocaml/xenopsd/lib/storage.ml

+31
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,37 @@ let deactivate task dp sr vdi vmdomid =
104104
(Printf.sprintf "VDI.deactivate %s" dp)
105105
(transform_exception (fun () -> Client.VDI.deactivate dbg dp sr vdi vmdomid))
106106

107+
let detach task dp =
108+
Xenops_task.with_subtask task
109+
(* TODO replace DP.destroy with something that just detaches *)
110+
(* This should work for now as, based on current usage of deactivate above during migrate,
111+
DP.destroy must have an idempotent deactivate, so can be safely used just for its detach *)
112+
(Printf.sprintf "detach (dp_destroy) %s" dp)
113+
(transform_exception (fun () ->
114+
let dbg = get_dbg task in
115+
let waiting_for_plugin = ref true in
116+
while !waiting_for_plugin do
117+
try
118+
Client.DP.destroy dbg dp false ;
119+
waiting_for_plugin := false
120+
with
121+
| Storage_interface.Storage_error (No_storage_plugin_for_sr _sr) as e
122+
->
123+
(* Since we have an activated disk in this SR, assume we are
124+
still waiting for xapi to register the SR's plugin. *)
125+
debug "Caught %s - waiting for xapi to register storage plugins."
126+
(Printexc.to_string e) ;
127+
Thread.delay 5.0
128+
| e ->
129+
(* Backends aren't supposed to return exceptions on
130+
deactivate/detach, but they frequently do. Log and ignore *)
131+
warn "Detach (dp_destroy) returned unexpected exception: %s"
132+
(Printexc.to_string e) ;
133+
waiting_for_plugin := false
134+
done
135+
)
136+
)
137+
107138
let dp_destroy task dp =
108139
Xenops_task.with_subtask task
109140
(Printf.sprintf "DP.destroy %s" dp)

Diff for: ocaml/xenopsd/lib/xenops_server.ml

+25-2
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@ type atomic =
130130
| VBD_epoch_end of (Vbd.id * disk)
131131
| VBD_set_qos of Vbd.id
132132
| VBD_unplug of Vbd.id * bool
133+
| VBD_deactivate of Vbd.id * bool
134+
| VBD_detach of Vbd.id
133135
| VBD_insert of Vbd.id * disk
134136
| VBD_set_active of Vbd.id * bool
135137
| VM_remove of Vm.id
@@ -211,6 +213,10 @@ let rec name_of_atomic = function
211213
"VBD_set_qos"
212214
| VBD_unplug _ ->
213215
"VBD_unplug"
216+
| VBD_deactivate _ ->
217+
"VBD_deactivate"
218+
| VBD_detach _ ->
219+
"VBD_detach"
214220
| VBD_insert _ ->
215221
"VBD_insert"
216222
| VBD_set_active _ ->
@@ -1609,6 +1615,13 @@ let split_plug_atomic id vbd_id =
16091615
nonempty_serial_unpacked "VBD.attach_and_activate" ~id
16101616
[VBD_attach vbd_id; VBD_activate vbd_id]
16111617

1618+
let split_unplug_atomic id vbd_id force =
1619+
if !xenopsd_vbd_plug_unplug_legacy then
1620+
VBD_unplug (vbd_id, force)
1621+
else
1622+
nonempty_serial_unpacked "VBD.deactivate_and_detach" ~id
1623+
[VBD_deactivate (vbd_id, force); VBD_detach vbd_id]
1624+
16121625
let rec atomics_of_operation = function
16131626
| VM_start (id, force) ->
16141627
let vbds_rw, vbds_ro = VBD_DB.vbds id |> vbd_plug_sets in
@@ -1697,7 +1710,7 @@ let rec atomics_of_operation = function
16971710
]
16981711
; parallel_concat "Devices.unplug" ~id
16991712
[
1700-
List.map (fun vbd -> VBD_unplug (vbd.Vbd.id, true)) vbds
1713+
List.map (fun vbd -> split_unplug_atomic id vbd.Vbd.id true) vbds
17011714
; List.map (fun vif -> VIF_unplug (vif.Vif.id, true)) vifs
17021715
; List.map (fun pci -> PCI_unplug pci.Pci.id) pcis
17031716
]
@@ -1859,7 +1872,7 @@ let rec atomics_of_operation = function
18591872
| VBD_hotplug id ->
18601873
[VBD_set_active (id, true); split_plug_atomic "VBD_hotplug" id]
18611874
| VBD_hotunplug (id, force) ->
1862-
[VBD_unplug (id, force); VBD_set_active (id, false)]
1875+
[split_unplug_atomic "VBD_hotunplug" id force; VBD_set_active (id, false)]
18631876
| VIF_hotplug id ->
18641877
[VIF_set_active (id, true); VIF_plug id]
18651878
| VIF_hotunplug (id, force) ->
@@ -2075,6 +2088,14 @@ let rec perform_atomic ~progress_callback ?result (op : atomic)
20752088
finally
20762089
(fun () -> B.VBD.unplug t (VBD_DB.vm_of id) (VBD_DB.read_exn id) force)
20772090
(fun () -> VBD_DB.signal id)
2091+
| VBD_deactivate (id, force) ->
2092+
debug "VBD.deactivate %s" (VBD_DB.string_of_id id) ;
2093+
B.VBD.deactivate t (VBD_DB.vm_of id) (VBD_DB.read_exn id) force
2094+
| VBD_detach id ->
2095+
debug "VBD.detach %s" (VBD_DB.string_of_id id) ;
2096+
finally
2097+
(fun () -> B.VBD.detach t (VBD_DB.vm_of id) (VBD_DB.read_exn id))
2098+
(fun () -> VBD_DB.signal id)
20782099
| VBD_insert (id, disk) -> (
20792100
(* NB this is also used to "refresh" ie signal a qemu that it should
20802101
re-open a device, useful for when a physical CDROM is inserted into the
@@ -2489,6 +2510,8 @@ and trigger_cleanup_after_failure_atom op t =
24892510
| VBD_epoch_end (id, _)
24902511
| VBD_set_qos id
24912512
| VBD_unplug (id, _)
2513+
| VBD_deactivate (id, _)
2514+
| VBD_detach id
24922515
| VBD_insert (id, _) ->
24932516
immediate_operation dbg (fst id) (VBD_check_state id)
24942517
| VIF_plug id

Diff for: ocaml/xenopsd/lib/xenops_server_plugin.ml

+4
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,10 @@ module type S = sig
215215

216216
val unplug : Xenops_task.task_handle -> Vm.id -> Vbd.t -> bool -> unit
217217

218+
val deactivate : Xenops_task.task_handle -> Vm.id -> Vbd.t -> bool -> unit
219+
220+
val detach : Xenops_task.task_handle -> Vm.id -> Vbd.t -> unit
221+
218222
val insert : Xenops_task.task_handle -> Vm.id -> Vbd.t -> disk -> unit
219223

220224
val eject : Xenops_task.task_handle -> Vm.id -> Vbd.t -> unit

Diff for: ocaml/xenopsd/lib/xenops_server_simulator.ml

+4
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,10 @@ module VBD = struct
681681

682682
let unplug _ vm vbd _ = with_lock m (remove_vbd vm vbd)
683683

684+
let deactivate _ vm vbd _ = with_lock m (remove_vbd vm vbd)
685+
686+
let detach _ _vm _vbd = ()
687+
684688
let insert _ _vm _vbd _disk = ()
685689

686690
let eject _ _vm _vbd = ()

Diff for: ocaml/xenopsd/lib/xenops_server_skeleton.ml

+4
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,10 @@ module VBD = struct
153153

154154
let unplug _ _ _ _ = unimplemented "VBD.unplug"
155155

156+
let deactivate _ _ _ _ = unimplemented "VBD.deactivate"
157+
158+
let detach _ _ _ = unimplemented "VBD.detach"
159+
156160
let insert _ _ _ _ = unimplemented "VBD.insert"
157161

158162
let eject _ _ _ = unimplemented "VBD.eject"

Diff for: ocaml/xenopsd/xc/xenops_server_xen.ml

+173
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,8 @@ module Storage = struct
458458

459459
let dp_destroy = dp_destroy
460460

461+
let detach = detach
462+
461463
let get_disk_by_name = get_disk_by_name
462464
end
463465

@@ -3659,6 +3661,8 @@ module VBD = struct
36593661
let vm = fst vbd.id in
36603662
Storage.activate ~xc ~xs task vm dp sr vdi
36613663

3664+
(* TODO could split out a _deactivate function here *)
3665+
36623666
let frontend_domid_of_device device =
36633667
device.Device_common.frontend.Device_common.domid
36643668

@@ -4230,6 +4234,175 @@ module VBD = struct
42304234
raise (Xenopsd_error (Device_detach_rejected ("VBD", id_of vbd, s)))
42314235
)
42324236

4237+
(* CP-53555: The deactivate half of VBD.unplug to allow them to be done separately *)
4238+
let deactivate task vm vbd force =
4239+
with_tracing ~task ~name:"VBD_deactivate" @@ fun () ->
4240+
with_xc_and_xs (fun xc xs ->
4241+
try
4242+
(* On destroying the datapath
4243+
4244+
1. if the device has already been shutdown and deactivated (as in
4245+
suspend) we must call DP.destroy here to avoid leaks
4246+
4247+
2. if the device is successfully shutdown here then we must call
4248+
DP.destroy because no-one else will
4249+
4250+
3. if the device shutdown is rejected then we should leave the DP
4251+
alone and rely on the event thread calling us again later. *)
4252+
let domid = domid_of_uuid ~xs (uuid_of_string vm) in
4253+
(* If the device is gone then we don't need to shut it down but we do
4254+
need to free any storage resources. *)
4255+
let dev =
4256+
try
4257+
Some (device_by_id xc xs vm (device_kind_of ~xs vbd) (id_of vbd))
4258+
with
4259+
| Xenopsd_error (Does_not_exist (_, _)) ->
4260+
debug "VM = %s; VBD = %s; Ignoring missing domain" vm (id_of vbd) ;
4261+
None
4262+
| Xenopsd_error Device_not_connected ->
4263+
debug "VM = %s; VBD = %s; Ignoring missing device" vm (id_of vbd) ;
4264+
None
4265+
in
4266+
let backend =
4267+
match dev with
4268+
| None ->
4269+
None
4270+
| Some dv -> (
4271+
match
4272+
Rpcmarshal.unmarshal typ_of_backend
4273+
(Device.Generic.get_private_key ~xs dv _vdi_id
4274+
|> Jsonrpc.of_string
4275+
)
4276+
with
4277+
| Ok x ->
4278+
x
4279+
| Error (`Msg m) ->
4280+
internal_error "Failed to unmarshal VBD backend: %s" m
4281+
)
4282+
in
4283+
Option.iter
4284+
(fun dev ->
4285+
if force && not (Device.can_surprise_remove ~xs dev) then
4286+
debug
4287+
"VM = %s; VBD = %s; Device is not surprise-removable \
4288+
(ignoring and removing anyway)"
4289+
vm (id_of vbd) ;
4290+
(* this happens on normal shutdown too *)
4291+
(* Case (1): success; Case (2): success; Case (3): an exception is
4292+
thrown *)
4293+
with_tracing ~task ~name:"VBD_deactivate_clean_shutdown"
4294+
@@ fun () ->
4295+
Xenops_task.with_subtask task
4296+
(Printf.sprintf "Vbd.clean_shutdown %s" (id_of vbd))
4297+
(fun () ->
4298+
(if force then Device.hard_shutdown else Device.clean_shutdown)
4299+
task ~xs dev
4300+
)
4301+
)
4302+
dev ;
4303+
(* We now have a shutdown device but an active DP: we should destroy
4304+
the DP if the backend is of type VDI *)
4305+
finally
4306+
(fun () ->
4307+
( with_tracing ~task ~name:"VBD_deactivate_release" @@ fun () ->
4308+
Option.iter
4309+
(fun dev ->
4310+
Xenops_task.with_subtask task
4311+
(Printf.sprintf "Vbd.release %s" (id_of vbd))
4312+
(fun () -> Device.Vbd.release task ~xc ~xs dev)
4313+
)
4314+
dev
4315+
) ;
4316+
(* If we have a qemu frontend, detach this too. *)
4317+
with_tracing ~task ~name:"VBD_deactivate_detach_qemu" @@ fun () ->
4318+
let _ =
4319+
DB.update vm
4320+
(Option.map (fun vm_t ->
4321+
let persistent = vm_t.VmExtra.persistent in
4322+
if List.mem_assoc vbd.Vbd.id persistent.VmExtra.qemu_vbds
4323+
then (
4324+
let _, qemu_vbd =
4325+
List.assoc vbd.Vbd.id persistent.VmExtra.qemu_vbds
4326+
in
4327+
(* destroy_vbd_frontend ignores 'refusing to close'
4328+
transients' *)
4329+
destroy_vbd_frontend ~xc ~xs task qemu_vbd ;
4330+
VmExtra.
4331+
{
4332+
persistent=
4333+
{
4334+
persistent with
4335+
qemu_vbds=
4336+
List.remove_assoc vbd.Vbd.id
4337+
persistent.qemu_vbds
4338+
}
4339+
}
4340+
) else
4341+
vm_t
4342+
)
4343+
)
4344+
in
4345+
()
4346+
)
4347+
(fun () ->
4348+
with_tracing ~task ~name:"VBD_deactivate_deactivate" @@ fun () ->
4349+
let vmid = Storage.vm_of_domid domid in
4350+
match (domid, backend) with
4351+
| Some x, Some (VDI path) ->
4352+
let sr, vdi = Storage.get_disk_by_name task path in
4353+
let dp = Storage.id_of (string_of_int x) vbd.id in
4354+
Storage.deactivate task dp sr vdi vmid
4355+
(* TODO Do we only need to deactivate VDIs, not Local or CD? *)
4356+
| _ ->
4357+
()
4358+
)
4359+
with Device_common.Device_error (_, s) ->
4360+
debug "Caught Device_error: %s" s ;
4361+
raise (Xenopsd_error (Device_detach_rejected ("VBD", id_of vbd, s)))
4362+
)
4363+
4364+
(* CP-53555: The detach half of VBD.unplug to allow them to be done separately *)
4365+
let detach task vm vbd =
4366+
with_tracing ~task ~name:"VBD_detach" @@ fun () ->
4367+
with_xc_and_xs (fun xc xs ->
4368+
let domid = domid_of_uuid ~xs (uuid_of_string vm) in
4369+
let dev =
4370+
try
4371+
Some (device_by_id xc xs vm (device_kind_of ~xs vbd) (id_of vbd))
4372+
with
4373+
| Xenopsd_error (Does_not_exist (_, _)) ->
4374+
debug "VM = %s; VBD = %s; Ignoring missing domain" vm (id_of vbd) ;
4375+
None
4376+
| Xenopsd_error Device_not_connected ->
4377+
debug "VM = %s; VBD = %s; Ignoring missing device" vm (id_of vbd) ;
4378+
None
4379+
in
4380+
let backend =
4381+
match dev with
4382+
| None ->
4383+
None
4384+
| Some dv -> (
4385+
match
4386+
Rpcmarshal.unmarshal typ_of_backend
4387+
(Device.Generic.get_private_key ~xs dv _vdi_id
4388+
|> Jsonrpc.of_string
4389+
)
4390+
with
4391+
| Ok x ->
4392+
x
4393+
| Error (`Msg m) ->
4394+
internal_error "Failed to unmarshal VBD backend: %s" m
4395+
)
4396+
in
4397+
with_tracing ~task ~name:"VBD_detach_dp_destroy" @@ fun () ->
4398+
match (domid, backend) with
4399+
| Some x, None | Some x, Some (VDI _) ->
4400+
Storage.detach task (Storage.id_of (string_of_int x) vbd.Vbd.id)
4401+
| _ ->
4402+
()
4403+
) ;
4404+
cleanup_attached_vdis vm vbd.id
4405+
42334406
let insert task vm vbd d =
42344407
on_frontend
42354408
(fun xc xs frontend_domid domain_type ->

0 commit comments

Comments
 (0)