From: Jonathan Davies Date: Tue, 14 Mar 2017 13:20:07 +0000 (+0000) Subject: oxenstored: support commit history tracking X-Git-Url: http://xenbits.xensource.com/gitweb?a=commitdiff_plain;h=a28b99d05c2f9e759091a315d65e818c68440d1d;p=xen.git oxenstored: support commit history tracking Add ability to track xenstore tree operations -- either non-transactional operations or committed transactions. For now, the call to actually retain commits is commented out because history can grow without bound. For now, we call record_commit for all non-transactional operations. A subsequent patch will make it retain only the ones with side-effects. Reported-by: Juergen Gross Signed-off-by: Jonathan Davies Signed-off-by: Thomas Sanders Reviewed-by: Christian Lindig --- diff --git a/tools/ocaml/xenstored/Makefile b/tools/ocaml/xenstored/Makefile index 3d045bb6f2..c92fcc1e87 100644 --- a/tools/ocaml/xenstored/Makefile +++ b/tools/ocaml/xenstored/Makefile @@ -47,6 +47,7 @@ OBJS = define \ domains \ connection \ connections \ + history \ parse_arg \ process \ xenstored diff --git a/tools/ocaml/xenstored/history.ml b/tools/ocaml/xenstored/history.ml new file mode 100644 index 0000000000..e4b4d70be7 --- /dev/null +++ b/tools/ocaml/xenstored/history.ml @@ -0,0 +1,43 @@ +(* + * Copyright (c) 2017 Citrix Systems Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; version 2.1 only. with the special + * exception on linking described in file LICENSE. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + *) + +type history_record = { + con: Connection.t; (* connection that made a change *) + tid: int; (* transaction id of the change (may be Transaction.none) *) + before: Store.t; (* the store before the change *) + after: Store.t; (* the store after the change *) + finish_count: int64; (* the commit-count at which the transaction finished *) +} + +let history : history_record list ref = ref [] + +(* Called from periodic_ops to ensure we don't discard symbols that are still needed. *) +(* There is scope for optimisation here, since in consecutive commits one commit's `after` + * is the same thing as the next commit's `before`, but not all commits in history are + * consecutive. *) +let mark_symbols () = + (* There are gaps where dom0's commits are missing. Otherwise we could assume that + * each element's `before` is the same thing as the next element's `after` + * since the next element is the previous commit *) + List.iter (fun hist_rec -> + Store.mark_symbols hist_rec.before; + Store.mark_symbols hist_rec.after; + ) + !history + +let push (x: history_record) = + let dom = x.con.Connection.dom in + match dom with + | None -> () (* treat socket connections as always free to conflict *) + | Some d -> if not (Domain.is_free_to_conflict d) then history := x :: !history diff --git a/tools/ocaml/xenstored/process.ml b/tools/ocaml/xenstored/process.ml index 0596be2b08..c38e3ad8b7 100644 --- a/tools/ocaml/xenstored/process.ml +++ b/tools/ocaml/xenstored/process.ml @@ -287,6 +287,16 @@ let write_response_log ~ty ~tid ~con ~response = | Packet.Reply x -> write_answer_log ~ty ~tid ~con ~data:x | Packet.Error e -> write_answer_log ~ty:(Xenbus.Xb.Op.Error) ~tid ~con ~data:e +let record_commit ~con ~tid ~before ~after = + let inc r = r := Int64.add 1L !r in + let finish_count = inc Transaction.counter; !Transaction.counter in + (* This call would leak memory if historic activity is retained forever + so can only be uncommented if history is guaranteed not to grow + unboundedly. + History.push {History.con=con; tid=tid; before=before; after=after; finish_count=finish_count} + *) + () + (* Replay a stored transaction against a fresh store, check the responses are all equivalent: if so, commit the transaction. Otherwise send the abort to the client. *) @@ -357,8 +367,14 @@ let do_transaction_end con t domains cons data = Connection.end_transaction con (Transaction.get_id t) commit in if not success then raise Transaction_again; - if commit then - process_watch (List.rev (Transaction.get_paths t)) cons + if commit then begin + process_watch (List.rev (Transaction.get_paths t)) cons; + match t.Transaction.ty with + | Transaction.No -> + () (* no need to record anything *) + | Transaction.Full(id, oldstore, cstore) -> + record_commit ~con ~tid:id ~before:oldstore ~after:cstore + end let do_introduce con t domains cons data = if not (Connection.is_dom0 con) @@ -441,7 +457,11 @@ let process_packet ~store ~cons ~doms ~con ~req = else Connection.get_transaction con tid in + + let before = Store.copy store in let response = input_handle_error ~cons ~doms ~fct ~con ~t ~req in + let after = Store.copy store in + if tid = Transaction.none then record_commit ~con ~tid ~before ~after; let response = try if tid <> Transaction.none then diff --git a/tools/ocaml/xenstored/xenstored.ml b/tools/ocaml/xenstored/xenstored.ml index daefa7cb03..be6a1ab403 100644 --- a/tools/ocaml/xenstored/xenstored.ml +++ b/tools/ocaml/xenstored/xenstored.ml @@ -386,6 +386,7 @@ let _ = Symbol.mark_all_as_unused (); Store.mark_symbols store; Connections.iter cons Connection.mark_symbols; + History.mark_symbols (); Symbol.garbage () end;