@@ -293,147 +293,11 @@ let refresh ~__context ~vbd ~vdi =
293293
294294open Threadext
295295open Pervasiveext
296+ open Fun
296297
297- (* VBD.pause and VBD.unpause are used for two purposes:
298- 1. to stop IO and give blkback/blktap a kick during disk online resize/ disk snapshot
299- 2. to stop IO while the background coalescing daemon moves metadata around
300- As explained in CA-24232, there is an issue where both purposes of call can happen simulataneously
301- and so we need to perform some serialisation otherwise the first 'unpause' will prematurely reactivate
302- the device.
303-
304- We assume that all client threads issue 'pause' then 'unpause' calls in sequence, never in parallel.
305- We assume that clients *rarely* fail between 'pause' and 'unpause' but in this extreme case, the
306- VM can always be force-shutdown (ie the VM is 'unlocked' while a disk is paused)
307-
308- We let one pause request through at a time and set vbd_pause_state.in_progress to true. Other
309- pause threads wait until in_progress is reset to false by device_is_unpaused.
310- We count the number of threads running VBD.pause in order to remove the vbd_pause_state from
311- the hashtable when the unpause happens.
312- *)
313-
314- (* One of these is stored per actively-paused VBD *)
315- type vbd_pause_state = {
316- m : Mutex .t ;
317- c : Condition .t ;
318- mutable in_progress : bool ; (* true if a VBD.pause thread is performing the operation *) (* protected by 'm' *)
319-
320- mutable blocked_threads : int ; (* counts the number of threads blocked in pause *) (* protected by 'paused_vbds_m' *)
321- }
322-
323- (* Table of vbd_pause_state records *)
324- let paused_vbds = Hashtbl. create 10
325- let paused_vbds_m = Mutex. create ()
326-
327- (* Either the pause just failed or an unpause has succeeded: wake up any blocked threads or cleanup if none are waiting *)
328- let device_is_unpaused self state =
329- (* If no threads are waiting in the queue to perform fresh pauses then we can delete the vbd_pause_state record.
330- If some thread(s) are waiting then we signal them. *)
331- Mutex. execute paused_vbds_m
332- (fun () ->
333- debug " device_is_unpaused blocked_threads=%d" state.blocked_threads;
334- if state.blocked_threads = 0
335- then (Hashtbl. remove paused_vbds self; debug " Removed hashtbl entry for %s" (Ref. string_of self))
336- else (Mutex. execute state.m (fun () -> state.in_progress < - false ); Condition. signal state.c))
337-
338- let pause_common ~__context ~vm ~self =
339-
340- (* XXX: these checks may be redundant since the message forwarding layer checks this stuff *)
341- let localhost = Helpers. get_localhost ~__context in
342-
343- (* Since we blocked not holding the per-VM mutex, we need to double-check that the VM is still present *)
344- if not (Helpers. is_running ~__context ~self: vm)
345- then raise (Api_errors. Server_error (Api_errors. vm_bad_power_state,
346- [ Ref. string_of vm;
347- Record_util. power_to_string `Running ;
348- Record_util. power_to_string (Db.VM. get_power_state ~__context ~self: vm) ]));
349-
350- if not (Db.VBD. get_currently_attached ~__context ~self )
351- then raise (Api_errors. Server_error (Api_errors. device_not_attached, [ Ref. string_of self ]));
352-
353- (* Make sure VM has not migrated *)
354- if Db.VM. get_resident_on ~__context ~self: vm <> localhost
355- then raise (Api_errors. Server_error (Api_errors. vm_not_resident_here, [ Ref. string_of vm; Ref. string_of localhost ]));
356-
357- let device = Xen_helpers. device_of_vbd ~__context ~self in
358- try
359- with_xs (fun xs -> Device.Vbd. pause ~xs device)
360- with Device. Device_shutdown | Device. Device_not_found ->
361- raise (Api_errors. Server_error (Api_errors. device_not_attached, [ Ref. string_of self ]))
362-
363- let pause ~__context ~self : string =
364- let vm = Db.VBD. get_VM ~__context ~self in
365-
366- (* 1. Find the current vbd pause state record or make a new one *)
367- let state = Mutex. execute paused_vbds_m
368- (fun () ->
369- let state =
370- if Hashtbl. mem paused_vbds self
371- then Hashtbl. find paused_vbds self
372- else
373- let state = { m = Mutex. create () ; c = Condition. create () ; in_progress = false ; blocked_threads = 0 } in
374- Hashtbl. replace paused_vbds self state;
375- state in
376- state.blocked_threads < - state.blocked_threads + 1 ;
377- state
378- ) in
379- (* Multiple VBD.pause threads may end up here. Note the vbd_pause_state record will linger until
380- the final unpause *)
381- try
382- finally
383- (fun () ->
384- (* Only one VBD.pause thread is allowed to acquire the 'in_progress' flag/lock *)
385- Mutex. execute state.m
386- (fun () ->
387- while state.in_progress do Condition. wait state.c state.m done ;
388- state.in_progress < - true );
389- (* One lucky VBD.pause thread will get here at a time *)
390- pause_common ~__context ~vm ~self
391- )
392- (* Decrement the 'blocked_threads' refcount *)
393- (fun () -> Mutex. execute paused_vbds_m (fun () -> state.blocked_threads < - state.blocked_threads - 1 ));
394- (* This thread returns leaving the 'in_progress' flag true -- this is the lock. *)
395-
396- with e ->
397- (* Something bad happened when we were trying to pause so unwind and cleanup *)
398- error " Unexpected error during VBD.pause: %s" (ExnHelper. string_of_exn e);
399- device_is_unpaused self state;
400- raise e
401-
402- let state_of_vbd self =
403- Mutex. execute paused_vbds_m
404- (fun () ->
405- if Hashtbl. mem paused_vbds self
406- then Some (Hashtbl. find paused_vbds self)
407- else None )
408-
409- let unpause ~__context ~self ~token =
410- (* Find the vbd_pause_state record which must exist if the client has called VBD.pause beforehand
411- UNLESS someone restarted xapi *)
412- let state = state_of_vbd self in
413-
414- (* Note we don't check that the client has waited for the VBD.pause to return: we trust the client not
415- to be stupid anyway by the nature of this API *)
416- let device = Xen_helpers. device_of_vbd ~__context ~self in
417- try
418- with_xs (fun xs -> Device.Vbd. unpause ~xs device token);
419- Opt. iter (device_is_unpaused self) state
420- with
421- | Device. Device_not_paused ->
422- debug " Ignoring Device_not_paused exception" ;
423- Opt. iter (device_is_unpaused self) state
424- | Device. Device_not_found ->
425- debug " Ignoring Device_not_found exception" ;
426- Opt. iter (device_is_unpaused self) state
427- | Device. Pause_token_mismatch ->
428- warn " Unpause left device paused because supplied token did not match"
429-
430- let flush ~__context self =
431- debug " Flushing vbd %s" (Ref. string_of self);
432- let token = pause ~__context ~self in
433- unpause ~__context ~self ~token
434-
435- (* * Called on domain destroy to signal any blocked pause threads to re-evaluate the state of the world
436- and give up *)
437- let clean_up_on_domain_destroy self =
438- let state = state_of_vbd self in
439- Opt. iter (device_is_unpaused self) state
298+ let pause ~__context ~self =
299+ let vdi = Db.VBD. get_VDI ~__context ~self in
300+ let sr = Db.VDI. get_SR ~__context ~self: vdi |> Ref. string_of in
301+ raise (Api_errors. Server_error (Api_errors. sr_operation_not_supported, [ sr ]))
302+
303+ let unpause = pause
0 commit comments