source: trunk/packages/xen-3.1/xen-3.1/tools/libxc/ia64/xc_ia64_linux_save.c @ 34

Last change on this file since 34 was 34, checked in by hartmans, 18 years ago

Add xen and xen-common

File size: 14.3 KB
Line 
1/******************************************************************************
2 * xc_ia64_linux_save.c
3 *
4 * Save the state of a running Linux session.
5 *
6 * Copyright (c) 2003, K A Fraser.
7 *  Rewritten for ia64 by Tristan Gingold <tristan.gingold@bull.net>
8 */
9
10#include <inttypes.h>
11#include <time.h>
12#include <stdlib.h>
13#include <unistd.h>
14#include <sys/time.h>
15
16#include "xg_private.h"
17
18/*
19** Default values for important tuning parameters. Can override by passing
20** non-zero replacement values to xc_linux_save().
21**
22** XXX SMH: should consider if want to be able to override MAX_MBIT_RATE too.
23**
24*/
25#define DEF_MAX_ITERS    (4 - 1)        /* limit us to 4 times round loop  */
26#define DEF_MAX_FACTOR   3              /* never send more than 3x nr_pfns */
27
28/*
29** During (live) save/migrate, we maintain a number of bitmaps to track
30** which pages we have to send, and to skip.
31*/
32
33#define BITS_PER_LONG (sizeof(unsigned long) * 8)
34
35#define BITMAP_ENTRY(_nr,_bmap) \
36   ((unsigned long *)(_bmap))[(_nr)/BITS_PER_LONG]
37
38#define BITMAP_SHIFT(_nr) ((_nr) % BITS_PER_LONG)
39
40static inline int test_bit (int nr, volatile void * addr)
41{
42    return (BITMAP_ENTRY(nr, addr) >> BITMAP_SHIFT(nr)) & 1;
43}
44
45static inline void clear_bit (int nr, volatile void * addr)
46{
47    BITMAP_ENTRY(nr, addr) &= ~(1UL << BITMAP_SHIFT(nr));
48}
49
50static inline void set_bit ( int nr, volatile void * addr)
51{
52    BITMAP_ENTRY(nr, addr) |= (1UL << BITMAP_SHIFT(nr));
53}
54
55/* total number of pages used by the current guest */
56static unsigned long max_pfn;
57
58static int xc_ia64_shadow_control(int xc_handle,
59                                  uint32_t domid,
60                                  unsigned int sop,
61                                  unsigned long *dirty_bitmap,
62                                  unsigned long pages,
63                                  xc_shadow_op_stats_t *stats)
64{
65    if (dirty_bitmap != NULL && pages > 0) {
66        int i;
67        unsigned char *bmap = (unsigned char *)dirty_bitmap;
68        unsigned long bmap_bytes =
69            ((pages + BITS_PER_LONG - 1) & ~(BITS_PER_LONG - 1)) / 8;
70        unsigned int bmap_pages = (bmap_bytes + PAGE_SIZE - 1) / PAGE_SIZE; 
71
72        /* Touch the page so that it is in the TC.
73           FIXME: use a more reliable method.  */
74        for (i = 0 ; i < bmap_pages ; i++)
75            bmap[i * PAGE_SIZE] = 0;
76        /* Because bmap is not page aligned (allocated by malloc), be sure the
77           last page is touched.  */
78        bmap[bmap_bytes - 1] = 0;
79    }
80
81    return xc_shadow_control(xc_handle, domid, sop,
82                             dirty_bitmap, pages, NULL, 0, stats);
83}
84
85static inline ssize_t
86write_exact(int fd, void *buf, size_t count)
87{
88    if (write(fd, buf, count) != count)
89        return 0;
90    return 1;
91}
92
93static int
94suspend_and_state(int (*suspend)(int), int xc_handle, int io_fd,
95                  int dom, xc_dominfo_t *info)
96{
97    int i = 0;
98
99    if (!(*suspend)(dom)) {
100        ERROR("Suspend request failed");
101        return -1;
102    }
103
104retry:
105
106    if (xc_domain_getinfo(xc_handle, dom, 1, info) != 1) {
107        ERROR("Could not get domain info");
108        return -1;
109    }
110
111    if (info->shutdown && info->shutdown_reason == SHUTDOWN_suspend)
112        return 0; // success
113
114    if (info->paused) {
115        // try unpausing domain, wait, and retest
116        xc_domain_unpause(xc_handle, dom);
117
118        ERROR("Domain was paused. Wait and re-test.");
119        usleep(10000);  // 10ms
120
121        goto retry;
122    }
123
124
125    if(++i < 100) {
126        ERROR("Retry suspend domain.");
127        usleep(10000);  // 10ms
128        goto retry;
129    }
130
131    ERROR("Unable to suspend domain.");
132
133    return -1;
134}
135
136int
137xc_domain_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
138               uint32_t max_factor, uint32_t flags, int (*suspend)(int),
139               int hvm, void *(*init_qemu_maps)(int, unsigned),
140               void (*qemu_flip_buffer)(int, int))
141{
142    DECLARE_DOMCTL;
143    xc_dominfo_t info;
144
145    int rc = 1;
146
147    //int live  = (flags & XCFLAGS_LIVE);
148    int debug = (flags & XCFLAGS_DEBUG);
149    int live  = (flags & XCFLAGS_LIVE);
150
151    /* The new domain's shared-info frame number. */
152    unsigned long shared_info_frame;
153
154    /* A copy of the CPU context of the guest. */
155    vcpu_guest_context_t ctxt;
156
157    unsigned long *page_array = NULL;
158
159    /* Live mapping of shared info structure */
160    shared_info_t *live_shinfo = NULL;
161
162    /* Iteration number.  */
163    int iter;
164
165    /* Number of pages sent in the last iteration (live only).  */
166    unsigned int sent_last_iter;
167
168    /* Number of pages sent (live only).  */
169    unsigned int total_sent;
170
171    /* Size of the shadow bitmap (live only).  */
172    unsigned int bitmap_size = 0;
173
174    /* True if last iteration.  */
175    int last_iter;
176
177    /* Bitmap of pages to be sent.  */
178    unsigned long *to_send = NULL;
179    /* Bitmap of pages not to be sent (because dirtied).  */
180    unsigned long *to_skip = NULL;
181
182    char *mem;
183
184    if (debug)
185        fprintf (stderr, "xc_linux_save (ia64): started dom=%d\n", dom);
186
187    /* If no explicit control parameters given, use defaults */
188    if (!max_iters)
189        max_iters = DEF_MAX_ITERS;
190    if (!max_factor)
191        max_factor = DEF_MAX_FACTOR;
192
193    //initialize_mbit_rate();
194
195    if (xc_domain_getinfo(xc_handle, dom, 1, &info) != 1) {
196        ERROR("Could not get domain info");
197        return 1;
198    }
199
200    shared_info_frame = info.shared_info_frame;
201
202#if 0
203    /* cheesy sanity check */
204    if ((info.max_memkb >> (PAGE_SHIFT - 10)) > max_mfn) {
205        ERROR("Invalid state record -- pfn count out of range: %lu",
206            (info.max_memkb >> (PAGE_SHIFT - 10)));
207        goto out;
208     }
209#endif
210
211    /* Map the shared info frame */
212    live_shinfo = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE,
213                                       PROT_READ, shared_info_frame);
214    if (!live_shinfo) {
215        ERROR("Couldn't map live_shinfo");
216        goto out;
217    }
218
219    max_pfn = info.max_memkb >> (PAGE_SHIFT - 10);
220
221    page_array = malloc(max_pfn * sizeof(unsigned long));
222    if (page_array == NULL) {
223        ERROR("Could not allocate memory");
224        goto out;
225    }
226
227    /* This is expected by xm restore.  */
228    if (!write_exact(io_fd, &max_pfn, sizeof(unsigned long))) {
229        ERROR("write: max_pfn");
230        goto out;
231    }
232
233    /* xc_linux_restore starts to read here.  */
234    /* Write a version number.  This can avoid searching for a stupid bug
235       if the format change.
236       The version is hard-coded, don't forget to change the restore code
237       too!  */
238    {
239        unsigned long version = 1;
240
241        if (!write_exact(io_fd, &version, sizeof(unsigned long))) {
242            ERROR("write: version");
243            goto out;
244        }
245    }
246
247    domctl.cmd = XEN_DOMCTL_arch_setup;
248    domctl.domain = (domid_t)dom;
249    domctl.u.arch_setup.flags = XEN_DOMAINSETUP_query;
250    if (xc_domctl(xc_handle, &domctl) < 0) {
251        ERROR("Could not get domain setup");
252        goto out;
253    }
254    if (!write_exact(io_fd, &domctl.u.arch_setup,
255                     sizeof(domctl.u.arch_setup))) {
256        ERROR("write: domain setup");
257        goto out;
258    }
259
260    /* Domain is still running at this point */
261    if (live) {
262
263        if (xc_ia64_shadow_control(xc_handle, dom,
264                                   XEN_DOMCTL_SHADOW_OP_ENABLE_LOGDIRTY,
265                                   NULL, 0, NULL ) < 0) {
266            ERROR("Couldn't enable shadow mode");
267            goto out;
268        }
269
270        last_iter = 0;
271
272        bitmap_size = ((max_pfn + BITS_PER_LONG-1) & ~(BITS_PER_LONG-1)) / 8;
273        to_send = malloc(bitmap_size);
274        to_skip = malloc(bitmap_size);
275
276        if (!to_send || !to_skip) {
277            ERROR("Couldn't allocate bitmap array");
278            goto out;
279        }
280
281        /* Initially all the pages must be sent.  */
282        memset(to_send, 0xff, bitmap_size);
283
284        if (mlock(to_send, bitmap_size)) {
285            ERROR("Unable to mlock to_send");
286            goto out;
287        }
288        if (mlock(to_skip, bitmap_size)) {
289            ERROR("Unable to mlock to_skip");
290            goto out;
291        }
292       
293    } else {
294
295        /* This is a non-live suspend. Issue the call back to get the
296           domain suspended */
297
298        last_iter = 1;
299
300        if (suspend_and_state(suspend, xc_handle, io_fd, dom, &info)) {
301            ERROR("Domain appears not to have suspended");
302            goto out;
303        }
304
305    }
306
307    sent_last_iter = max_pfn;
308    total_sent = 0;
309
310    for (iter = 1; ; iter++) {
311        unsigned int sent_this_iter, skip_this_iter;
312        unsigned long N;
313
314        sent_this_iter = 0;
315        skip_this_iter = 0;
316
317        /* Get the pfn list, as it may change.  */
318        if (xc_ia64_get_pfn_list(xc_handle, dom, page_array,
319                                 0, max_pfn) != max_pfn) {
320            ERROR("Could not get the page frame list");
321            goto out;
322        }
323
324        /* Dirtied pages won't be saved.
325           slightly wasteful to peek the whole array evey time,
326           but this is fast enough for the moment. */
327        if (!last_iter) {
328            if (xc_ia64_shadow_control(xc_handle, dom,
329                                       XEN_DOMCTL_SHADOW_OP_PEEK,
330                                       to_skip, max_pfn, NULL) != max_pfn) {
331                ERROR("Error peeking shadow bitmap");
332                goto out;
333            }
334        }
335
336        /* Start writing out the saved-domain record. */
337        for (N = 0; N < max_pfn; N++) {
338            if (page_array[N] == INVALID_MFN)
339                continue;
340            if (!last_iter) {
341                if (test_bit(N, to_skip) && test_bit(N, to_send))
342                    skip_this_iter++;
343                if (test_bit(N, to_skip) || !test_bit(N, to_send))
344                    continue;
345            }
346
347            if (debug)
348                fprintf(stderr, "xc_linux_save: page %lx (%lu/%lu)\n",
349                        page_array[N], N, max_pfn);
350
351            mem = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE,
352                                       PROT_READ|PROT_WRITE, N);
353            if (mem == NULL) {
354                /* The page may have move.
355                   It will be remarked dirty.
356                   FIXME: to be tracked.  */
357                fprintf(stderr, "cannot map mfn page %lx gpfn %lx: %s\n",
358                        page_array[N], N, safe_strerror(errno));
359                continue;
360            }
361
362            if (!write_exact(io_fd, &N, sizeof(N))) {
363                ERROR("write: max_pfn");
364                goto out;
365            }
366
367            if (write(io_fd, mem, PAGE_SIZE) != PAGE_SIZE) {
368                ERROR("Error when writing to state file (5)");
369                goto out;
370            }
371            munmap(mem, PAGE_SIZE);
372            sent_this_iter++;
373            total_sent++;
374        }
375
376        if (last_iter)
377            break;
378
379        DPRINTF(" %d: sent %d, skipped %d\n",
380                iter, sent_this_iter, skip_this_iter );
381
382        if (live) {
383            if ( /* ((sent_this_iter > sent_last_iter) && RATE_IS_MAX()) || */
384                (iter >= max_iters) || (sent_this_iter+skip_this_iter < 50) ||
385                (total_sent > max_pfn*max_factor)) {
386                DPRINTF("Start last iteration\n");
387                last_iter = 1;
388
389                if (suspend_and_state(suspend, xc_handle, io_fd, dom, &info)) {
390                    ERROR("Domain appears not to have suspended");
391                    goto out;
392                }
393            }
394
395            /* Pages to be sent are pages which were dirty.  */
396            if (xc_ia64_shadow_control(xc_handle, dom,
397                                       XEN_DOMCTL_SHADOW_OP_CLEAN,
398                                       to_send, max_pfn, NULL ) != max_pfn) {
399                ERROR("Error flushing shadow PT");
400                goto out;
401            }
402
403            sent_last_iter = sent_this_iter;
404
405            //print_stats(xc_handle, dom, sent_this_iter, &stats, 1);
406        }
407
408    }
409
410    fprintf (stderr, "All memory is saved\n");
411
412    /* terminate */
413    {
414        unsigned long pfn = INVALID_MFN;
415        if (!write_exact(io_fd, &pfn, sizeof(pfn))) {
416            ERROR("Error when writing to state file (6)");
417            goto out;
418        }
419    }
420
421    /* Send through a list of all the PFNs that were not in map at the close */
422    {
423        unsigned int i,j;
424        unsigned long pfntab[1024];
425
426        for (i = 0, j = 0; i < max_pfn; i++) {
427            if (page_array[i] == INVALID_MFN)
428                j++;
429        }
430
431        if (!write_exact(io_fd, &j, sizeof(unsigned int))) {
432            ERROR("Error when writing to state file (6a)");
433            goto out;
434        }
435
436        for (i = 0, j = 0; i < max_pfn; ) {
437
438            if (page_array[i] == INVALID_MFN)
439                pfntab[j++] = i;
440
441            i++;
442            if (j == 1024 || i == max_pfn) {
443                if (!write_exact(io_fd, &pfntab, sizeof(unsigned long)*j)) {
444                    ERROR("Error when writing to state file (6b)");
445                    goto out;
446                }
447                j = 0;
448            }
449        }
450
451    }
452
453    if (xc_vcpu_getcontext(xc_handle, dom, 0, &ctxt)) {
454        ERROR("Could not get vcpu context");
455        goto out;
456    }
457
458    if (!write_exact(io_fd, &ctxt, sizeof(ctxt))) {
459        ERROR("Error when writing to state file (1)");
460        goto out;
461    }
462
463    fprintf(stderr, "ip=%016lx, b0=%016lx\n", ctxt.user_regs.cr_iip,
464            ctxt.user_regs.b0);
465
466    mem = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE,
467                               PROT_READ|PROT_WRITE, ctxt.privregs_pfn);
468    if (mem == NULL) {
469        ERROR("cannot map privreg page");
470        goto out;
471    }
472    if (write(io_fd, mem, PAGE_SIZE) != PAGE_SIZE) {
473        ERROR("Error when writing privreg to state file (5)");
474        goto out;
475    }
476    munmap(mem, PAGE_SIZE);   
477
478    if (!write_exact(io_fd, live_shinfo, PAGE_SIZE)) {
479        ERROR("Error when writing to state file (1)");
480        goto out;
481    }
482
483    /* Success! */
484    rc = 0;
485
486 out:
487
488    if (live) {
489        if (xc_ia64_shadow_control(xc_handle, dom, XEN_DOMCTL_SHADOW_OP_OFF,
490                                   NULL, 0, NULL ) < 0) {
491            DPRINTF("Warning - couldn't disable shadow mode");
492        }
493    }
494
495    free(page_array);
496    free(to_send);
497    free(to_skip);
498    if (live_shinfo)
499        munmap(live_shinfo, PAGE_SIZE);
500
501    fprintf(stderr,"Save exit rc=%d\n",rc);
502
503    return !!rc;
504}
505
506/*
507 * Local variables:
508 * mode: C
509 * c-set-style: "BSD"
510 * c-basic-offset: 4
511 * tab-width: 4
512 * indent-tabs-mode: nil
513 * End:
514 */
Note: See TracBrowser for help on using the repository browser.