Add list_head helper library.
authorRichard W.M. Jones <rjones@redhat.com>
Tue, 12 Aug 2008 19:11:54 +0000 (20:11 +0100)
committerRichard W.M. Jones <rjones@redhat.com>
Tue, 12 Aug 2008 19:11:54 +0000 (20:11 +0100)
 - Reimplement virt_mem_tasks using this library.

MANIFEST
lib/.depend
lib/Makefile.in
lib/virt_mem_list_head.ml [new file with mode: 0644]
lib/virt_mem_list_head.mli [new file with mode: 0644]
lib/virt_mem_tasks.ml

index 5c95026..8ec273c 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -33,6 +33,8 @@ lib/virt_mem_kallsyms.mli
 lib/virt_mem_kernels.ml
 lib/virt_mem_ksyms.ml
 lib/virt_mem_ksyms.mli
+lib/virt_mem_list_head.ml
+lib/virt_mem_list_head.mli
 lib/virt_mem.ml
 lib/virt_mem.mli
 lib/virt_mem_mmap.ml
index 6262905..a740b52 100644 (file)
@@ -3,6 +3,7 @@ kernel_net.cmi: virt_mem_types.cmi virt_mem_mmap.cmi
 kernel_task_struct.cmi: virt_mem_types.cmi virt_mem_mmap.cmi 
 virt_mem_kallsyms.cmi: virt_mem_types.cmi 
 virt_mem_ksyms.cmi: virt_mem_types.cmi 
+virt_mem_list_head.cmi: virt_mem_types.cmi virt_mem_mmap.cmi 
 virt_mem.cmi: virt_mem_types.cmi 
 virt_mem_mmap.cmi: virt_mem_utils.cmo 
 virt_mem_net_devices.cmi: virt_mem_types.cmi 
@@ -37,6 +38,10 @@ virt_mem_ksyms.cmo: virt_mem_utils.cmo virt_mem_types.cmi virt_mem_mmap.cmi \
     virt_mem_gettext.cmo virt_mem_ksyms.cmi 
 virt_mem_ksyms.cmx: virt_mem_utils.cmx virt_mem_types.cmx virt_mem_mmap.cmx \
     virt_mem_gettext.cmx virt_mem_ksyms.cmi 
+virt_mem_list_head.cmo: virt_mem_utils.cmo virt_mem_types.cmi \
+    virt_mem_mmap.cmi virt_mem_list_head.cmi 
+virt_mem_list_head.cmx: virt_mem_utils.cmx virt_mem_types.cmx \
+    virt_mem_mmap.cmx virt_mem_list_head.cmi 
 virt_mem.cmo: virt_mem_version.cmo virt_mem_utsname.cmi virt_mem_utils.cmo \
     virt_mem_types.cmi virt_mem_tasks.cmi virt_mem_net_devices.cmi \
     virt_mem_mmap.cmi virt_mem_ksyms.cmi virt_mem_kernels.cmo \
@@ -53,10 +58,12 @@ virt_mem_net_devices.cmo: virt_mem_utils.cmo virt_mem_types.cmi \
 virt_mem_net_devices.cmx: virt_mem_utils.cmx virt_mem_types.cmx \
     virt_mem_mmap.cmx virt_mem_gettext.cmx kernel_net_device.cmx \
     kernel_net.cmx virt_mem_net_devices.cmi 
-virt_mem_tasks.cmo: virt_mem_utils.cmo virt_mem_types.cmi virt_mem_mmap.cmi \
-    virt_mem_gettext.cmo kernel_task_struct.cmi virt_mem_tasks.cmi 
-virt_mem_tasks.cmx: virt_mem_utils.cmx virt_mem_types.cmx virt_mem_mmap.cmx \
-    virt_mem_gettext.cmx kernel_task_struct.cmx virt_mem_tasks.cmi 
+virt_mem_tasks.cmo: virt_mem_utils.cmo virt_mem_types.cmi \
+    virt_mem_list_head.cmi virt_mem_gettext.cmo kernel_task_struct.cmi \
+    virt_mem_tasks.cmi 
+virt_mem_tasks.cmx: virt_mem_utils.cmx virt_mem_types.cmx \
+    virt_mem_list_head.cmx virt_mem_gettext.cmx kernel_task_struct.cmx \
+    virt_mem_tasks.cmi 
 virt_mem_types.cmo: virt_mem_utils.cmo virt_mem_mmap.cmi virt_mem_types.cmi 
 virt_mem_types.cmx: virt_mem_utils.cmx virt_mem_mmap.cmx virt_mem_types.cmi 
 virt_mem_utsname.cmo: virt_mem_utils.cmo virt_mem_types.cmi virt_mem_mmap.cmi \
index b1f09d2..ee50a4c 100644 (file)
@@ -54,6 +54,7 @@ OBJS          = virt_mem_gettext.cmo \
                  virt_mem_mmap_c.o \
                  virt_mem_mmap.cmo \
                  virt_mem_types.cmo \
+                 virt_mem_list_head.cmo \
                  kernel_task_struct.cmo \
                  kernel_net_device.cmo \
                  kernel_net.cmo \
diff --git a/lib/virt_mem_list_head.ml b/lib/virt_mem_list_head.ml
new file mode 100644 (file)
index 0000000..de16a16
--- /dev/null
@@ -0,0 +1,89 @@
+(* Memory info command for virtual domains.
+   (C) Copyright 2008 Richard W.M. Jones, Red Hat Inc.
+   http://libvirt.org/
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   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 General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *)
+
+(* This code will work provided list_head always contains just a
+ * 'next' and 'prev' pointer.  If it changes, then we'll have to
+ * import the struct list_head from the kernel version, just like
+ * every other structure. (XXX)
+ *)
+
+open Printf
+
+open Virt_mem_utils
+open Virt_mem_types
+
+type t =
+    image                      (* Kernel image. *)
+    * int64                    (* Pointer to start of head struct. *)
+    * int64                    (* Offset. *)
+
+let create_base image head offset =
+  let offset = Int64.of_int offset in
+  (image, head, offset)
+
+let create image head offset =
+  let offset = Int64.of_int offset in
+  let head = head -^ offset in
+  (image, head, offset)
+
+let get_next_ptr image addr offset =
+  let addr = addr +^ offset in
+  let addr = Virt_mem_mmap.follow_pointer image.mem addr in
+  let addr = addr -^ offset in
+  addr
+
+let load (image, head, offset) f =
+  let rec loop image addr =
+    if addr <> head then (
+      let image = f image addr in
+      let addr = get_next_ptr image addr offset in
+      loop image addr
+    )
+    else image
+  in
+  let image = loop image (get_next_ptr image head offset) in
+  image, (image, head, offset)
+
+let load_all t size =
+  let f image addr =
+    let mapped = Virt_mem_mmap.is_mapped_range image.mem addr size in
+    let image =
+      if not mapped then Virt_mem_types.load_memory image addr size
+      else image in
+    image
+  in
+  load t f
+
+let fold (image, head, offset) b f =
+  let rec loop b addr =
+    if addr <> head then (
+      let b = f b addr in
+      let addr = get_next_ptr image addr offset in
+      loop b addr
+    )
+    else b
+  in
+  loop b (get_next_ptr image head offset)
+
+(* Iter and map can be implemented in terms of fold. *)
+let iter t f =
+  fold t () (fun () addr -> let () = f addr in ())
+
+let map t f =
+  List.rev (fold t [] (fun xs addr -> let x = f addr in x :: xs))
diff --git a/lib/virt_mem_list_head.mli b/lib/virt_mem_list_head.mli
new file mode 100644 (file)
index 0000000..3bf4246
--- /dev/null
@@ -0,0 +1,73 @@
+(** Handle struct list_head linked lists. *)
+(* Memory info command for virtual domains.
+   (C) Copyright 2008 Richard W.M. Jones, Red Hat Inc.
+   http://libvirt.org/
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   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 General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *)
+
+type t
+
+val create : Virt_mem_types.image -> Virt_mem_mmap.addr -> int -> t
+  (** [create image head offset] creates a list_head handle.
+
+      [head] is the address of the base [list_head] in kernel memory.
+
+      [offset] is the offset of the [list_head] within the whole
+      structure.
+
+      Note that [head] may be an isolated [struct list_head] or
+      it may be a member of a structure.  In the case where the
+      head is in a structure, the head structure is ignored (as is
+      generally the case in kernel code too).
+
+      If the memory might not have been loaded yet, you should
+      call {!load} or {!load_all} immediately after this.
+  *)
+
+val create_base : Virt_mem_types.image -> Virt_mem_mmap.addr -> int -> t
+  (** Same as {!create} but the address passed in is the base address
+      of the head structure {i not} the address of the list_head
+      within that structure.
+  *)
+
+val load : t ->
+  (Virt_mem_types.image -> Virt_mem_mmap.addr -> Virt_mem_types.image) ->
+  Virt_mem_types.image * t
+  (** This call allows you to load the kernel memory for each
+      structure in the list.
+
+      You supply a function to do the loading, given the
+      address of each structure in turn.  This returns an
+      updated memory map and {!t}.
+  *)
+
+val load_all : t -> int -> Virt_mem_types.image * t
+  (** More convenient version of {!load} where you specify the
+      structure size, and this function does all the rest.
+  *)
+
+val iter : t -> (Virt_mem_mmap.addr -> unit) -> unit
+  (** [iter t f] applies function [f] to every element of the list [t]. *)
+
+val map : t -> (Virt_mem_mmap.addr -> 'a) -> 'a list
+  (** [map t f] applies function [f] to every element of the list [t] and
+      returns a list of the results.
+  *)
+
+val fold : t -> 'a -> ('a -> Virt_mem_mmap.addr -> 'a) -> 'a
+  (** [fold t b f] folds function [f] over every element of the list [t]
+      with [b] as the base case.
+  *)
index c2ae54e..b75a711 100644 (file)
@@ -36,7 +36,7 @@ supported Linux distribution, see this page about adding support:
       image.domname kernel_version;
     image, None
   ) else (
-    let task_struct_size = task_struct_size kernel_version in
+    let size = task_struct_size kernel_version in
 
     let init_task_addr =
       try Some (Ksymmap.find "init_task" ksymmap)
@@ -47,33 +47,22 @@ supported Linux distribution, see this page about adding support:
     match init_task_addr with
     | None -> image, None
     | Some init_task_addr ->
-       let init_task =
-         get_task_struct kernel_version image.mem init_task_addr in
+       let { field_offset = offset } =
+         field_signature_of_task_struct_tasks'next kernel_version in
 
-       (* Starting at init_task, navigate through the linked list of
-        * tasks (through tasks.next).  Map them into memory and load
-        * them into a list.
-        *)
-       let image, tasks =
-         let rec loop i image acc task =
-           if i <= max_tasks then (
-             let next = task.task_struct_tasks'next in
-             if next <> init_task_addr then (
-               let mapped =
-                 Virt_mem_mmap.is_mapped_range image.mem next task_struct_size in
-               let image =
-                 if not mapped then
-                   Virt_mem_types.load_memory image next task_struct_size
-                 else
-                   image in
-               let task = get_task_struct kernel_version image.mem next in
-               loop (i+1) image (task :: acc) task
-             ) else
-               image, acc
-           ) else
-             failwith (sprintf (f_"%s: too many tasks") image.domname)
-         in
-         loop 0 image [] init_task in
+       let lh = Virt_mem_list_head.create_base image init_task_addr offset in
+       let image, lh = Virt_mem_list_head.load_all lh size in
+
+       let tasks, _ =
+         Virt_mem_list_head.fold lh ([], 0) (
+           fun (tasks, i) addr ->
+             if i > max_tasks then
+               failwith (sprintf (f_"%s: too many tasks") image.domname);
+
+             let task = get_task_struct kernel_version image.mem addr in
+             let tasks = task :: tasks in
+             (tasks, i+1)
+         ) in
 
        (* Convert to the internal format. *)
        let tasks = List.rev_map (