direct-io.hg

view tools/libxc/xc_core.c @ 12765:2dd4569e0640

[LIBXC] Add an error reporting API to the libxc library.

- An 'xc_error' struct is used to pass around error
details. Currently contains two members 'code' an enumeration of
error types, and 'message' a free text description of the specific
problem.

- The xc_get_last_error() method returns a const pointer to the
internal instance of this struct manged by libxc. By returning a
const pointer we can add extra members to the end of the struct at
any time without worrying about ABI of callers. This will let us
provide more fine-grained info if needed in the future.

- The xc_error instance is statically defined inside libxc and marked
__thread. This ensures that errors are recorded per-thread, and
that when dealing with errors we never need to call malloc - all
storage needed is statically allocated.

- The xc_clear_last_error() method resets any currently recorded
error details

- The xc_error_code_to_desc() method converts the integer error code
into a generic user facing messsage. eg "Invalid kernel". Together
with the 'message' field from xc_error, this provides the user
visible feedback. eg "Invalid kernel: Non PAE-kernel on PAE host."

- A callback can be registered with xc_set_error_handler to receive
notification whenever an error is recorded, rather than querying
for error details after the fact with xc_get_last_error

- If built with -DDEBUG set, a default error handler will be
registered which calls fprintf(stderr), thus maintaining current
behaviour of logging errors to stderr during developer builds.

- The python binding for libxc is updated to use xc_get_last_error
to pull out error details whenever appropriate, instead of
returning info based on 'errno'

- The xc_set_error method is private to libxc internals, and is used
for setting error details

- The ERROR and PERROR macros have been updated to call xc_set_error
automatically specifying XC_INTERNAL_ERROR as the error code. This
gives a generic error report for all current failure points

- Some uses of the ERROR macro have been replaced with explicit
calls to xc_set_error to enable finer grained error reporting. In
particular the code dealing with invalid kernel types uses this
to report about PAE/architecture/wordsize mismatches

The patch has been tested by calling xm create against a varietry of
config files defining invalid kernels of various kinds. It has also
been tested with libvirt talking to xend. In both cases the error
messages were propagated all the way back up the stack.

There is only one place where I need to do further work. The suspend
& restore APIs in Xend invoke external helper programs rather than
calling libxc directly. This means that error details are essentially
lost. Since there is already code in XenD which scans STDERR from
these programs I will investigate adapting this to extract actual
error messages from these helpers.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
author kfraser@localhost.localdomain
date Thu Dec 07 11:36:26 2006 +0000 (2006-12-07)
parents cfb1136ee8f7
children da87dc126b33
line source
1 #include "xg_private.h"
2 #include <stdlib.h>
3 #include <unistd.h>
5 /* number of pages to write at a time */
6 #define DUMP_INCREMENT (4 * 1024)
7 #define round_pgup(_p) (((_p)+(PAGE_SIZE-1))&PAGE_MASK)
9 static int
10 copy_from_domain_page(int xc_handle,
11 uint32_t domid,
12 unsigned long mfn,
13 void *dst_page)
14 {
15 void *vaddr = xc_map_foreign_range(
16 xc_handle, domid, PAGE_SIZE, PROT_READ, mfn);
17 if ( vaddr == NULL )
18 return -1;
19 memcpy(dst_page, vaddr, PAGE_SIZE);
20 munmap(vaddr, PAGE_SIZE);
21 return 0;
22 }
24 int
25 xc_domain_dumpcore_via_callback(int xc_handle,
26 uint32_t domid,
27 void *args,
28 dumpcore_rtn_t dump_rtn)
29 {
30 unsigned long nr_pages;
31 xen_pfn_t *page_array = NULL;
32 xc_dominfo_t info;
33 int i, nr_vcpus = 0;
34 char *dump_mem, *dump_mem_start = NULL;
35 struct xc_core_header header;
36 vcpu_guest_context_t ctxt[MAX_VIRT_CPUS];
37 char dummy[PAGE_SIZE];
38 int dummy_len;
39 int sts;
41 if ( (dump_mem_start = malloc(DUMP_INCREMENT*PAGE_SIZE)) == NULL )
42 {
43 PERROR("Could not allocate dump_mem");
44 goto error_out;
45 }
47 if ( xc_domain_getinfo(xc_handle, domid, 1, &info) != 1 )
48 {
49 PERROR("Could not get info for domain");
50 goto error_out;
51 }
53 if ( domid != info.domid )
54 {
55 PERROR("Domain %d does not exist", domid);
56 goto error_out;
57 }
59 for ( i = 0; i <= info.max_vcpu_id; i++ )
60 if ( xc_vcpu_getcontext(xc_handle, domid, i, &ctxt[nr_vcpus]) == 0)
61 nr_vcpus++;
63 nr_pages = info.nr_pages;
65 header.xch_magic = info.hvm ? XC_CORE_MAGIC_HVM : XC_CORE_MAGIC;
66 header.xch_nr_vcpus = nr_vcpus;
67 header.xch_nr_pages = nr_pages;
68 header.xch_ctxt_offset = sizeof(struct xc_core_header);
69 header.xch_index_offset = sizeof(struct xc_core_header) +
70 sizeof(vcpu_guest_context_t)*nr_vcpus;
71 dummy_len = (sizeof(struct xc_core_header) +
72 (sizeof(vcpu_guest_context_t) * nr_vcpus) +
73 (nr_pages * sizeof(xen_pfn_t)));
74 header.xch_pages_offset = round_pgup(dummy_len);
76 sts = dump_rtn(args, (char *)&header, sizeof(struct xc_core_header));
77 if ( sts != 0 )
78 goto error_out;
80 sts = dump_rtn(args, (char *)&ctxt, sizeof(ctxt[0]) * nr_vcpus);
81 if ( sts != 0 )
82 goto error_out;
84 if ( (page_array = malloc(nr_pages * sizeof(xen_pfn_t))) == NULL )
85 {
86 IPRINTF("Could not allocate memory\n");
87 goto error_out;
88 }
89 if ( xc_get_pfn_list(xc_handle, domid, page_array, nr_pages) != nr_pages )
90 {
91 IPRINTF("Could not get the page frame list\n");
92 goto error_out;
93 }
94 sts = dump_rtn(args, (char *)page_array, nr_pages * sizeof(xen_pfn_t));
95 if ( sts != 0 )
96 goto error_out;
98 /* Pad the output data to page alignment. */
99 memset(dummy, 0, PAGE_SIZE);
100 sts = dump_rtn(args, dummy, header.xch_pages_offset - dummy_len);
101 if ( sts != 0 )
102 goto error_out;
104 for ( dump_mem = dump_mem_start, i = 0; i < nr_pages; i++ )
105 {
106 copy_from_domain_page(xc_handle, domid, page_array[i], dump_mem);
107 dump_mem += PAGE_SIZE;
108 if ( ((i + 1) % DUMP_INCREMENT == 0) || ((i + 1) == nr_pages) )
109 {
110 sts = dump_rtn(args, dump_mem_start, dump_mem - dump_mem_start);
111 if ( sts != 0 )
112 goto error_out;
113 dump_mem = dump_mem_start;
114 }
115 }
117 free(dump_mem_start);
118 free(page_array);
119 return 0;
121 error_out:
122 free(dump_mem_start);
123 free(page_array);
124 return -1;
125 }
127 /* Callback args for writing to a local dump file. */
128 struct dump_args {
129 int fd;
130 };
132 /* Callback routine for writing to a local dump file. */
133 static int local_file_dump(void *args, char *buffer, unsigned int length)
134 {
135 struct dump_args *da = args;
136 int bytes, offset;
138 for ( offset = 0; offset < length; offset += bytes )
139 {
140 bytes = write(da->fd, &buffer[offset], length-offset);
141 if ( bytes <= 0 )
142 {
143 PERROR("Failed to write buffer: %s", strerror(errno));
144 return -errno;
145 }
146 }
148 return 0;
149 }
151 int
152 xc_domain_dumpcore(int xc_handle,
153 uint32_t domid,
154 const char *corename)
155 {
156 struct dump_args da;
157 int sts;
159 if ( (da.fd = open(corename, O_CREAT|O_RDWR, S_IWUSR|S_IRUSR)) < 0 )
160 {
161 PERROR("Could not open corefile %s: %s", corename, strerror(errno));
162 return -errno;
163 }
165 sts = xc_domain_dumpcore_via_callback(
166 xc_handle, domid, &da, &local_file_dump);
168 close(da.fd);
170 return sts;
171 }
173 /*
174 * Local variables:
175 * mode: C
176 * c-set-style: "BSD"
177 * c-basic-offset: 4
178 * tab-width: 4
179 * indent-tabs-mode: nil
180 * End:
181 */