From: Paolo Bonzini <pbonzini@redhat.com>
To: Juan Quintela <quintela@redhat.com>
Cc: qemu-devel@nongnu.org
Subject: Re: [Qemu-devel] [PATCH 16/30] migration: move buffered_file.c code into migration.c
Date: Thu, 18 Oct 2012 10:57:22 +0200 [thread overview]
Message-ID: <507FC472.9020700@redhat.com> (raw)
In-Reply-To: <1350545426-23172-17-git-send-email-quintela@redhat.com>
Il 18/10/2012 09:30, Juan Quintela ha scritto:
> This only moves the code (also from buffered_file.h to migration.h).
> Fix whitespace until checkpatch is happy.
While I agree with this patch, it is also a conflict magnet. My
migration-in-a-coroutine cleanups will touch buffered_file.c, you're
warned...
Paolo
> Signed-off-by: Juan Quintela <quintela@redhat.com>
> ---
> Makefile.objs | 2 +-
> buffered_file.c | 244 --------------------------------------------------------
> buffered_file.h | 22 -----
> migration.c | 218 +++++++++++++++++++++++++++++++++++++++++++++++++-
> migration.h | 1 +
> 5 files changed, 219 insertions(+), 268 deletions(-)
> delete mode 100644 buffered_file.c
> delete mode 100644 buffered_file.h
>
> diff --git a/Makefile.objs b/Makefile.objs
> index 74b3542..3de8f27 100644
> --- a/Makefile.objs
> +++ b/Makefile.objs
> @@ -73,7 +73,7 @@ extra-obj-$(CONFIG_LINUX) += fsdev/
>
> common-obj-y += tcg-runtime.o host-utils.o main-loop.o
> common-obj-y += input.o
> -common-obj-y += buffered_file.o migration.o migration-tcp.o
> +common-obj-y += migration.o migration-tcp.o
> common-obj-y += qemu-char.o #aio.o
> common-obj-y += block-migration.o iohandler.o
> common-obj-y += pflib.o
> diff --git a/buffered_file.c b/buffered_file.c
> deleted file mode 100644
> index c21f847..0000000
> --- a/buffered_file.c
> +++ /dev/null
> @@ -1,244 +0,0 @@
> -/*
> - * QEMU buffered QEMUFile
> - *
> - * Copyright IBM, Corp. 2008
> - *
> - * Authors:
> - * Anthony Liguori <aliguori@us.ibm.com>
> - *
> - * This work is licensed under the terms of the GNU GPL, version 2. See
> - * the COPYING file in the top-level directory.
> - *
> - * Contributions after 2012-01-13 are licensed under the terms of the
> - * GNU GPL, version 2 or (at your option) any later version.
> - */
> -
> -#include "qemu-common.h"
> -#include "hw/hw.h"
> -#include "qemu-timer.h"
> -#include "qemu-char.h"
> -#include "buffered_file.h"
> -#include "qemu-thread.h"
> -
> -//#define DEBUG_BUFFERED_FILE
> -
> -typedef struct QEMUFileBuffered
> -{
> - MigrationState *migration_state;
> - QEMUFile *file;
> - size_t bytes_xfer;
> - size_t xfer_limit;
> - uint8_t *buffer;
> - size_t buffer_size;
> - size_t buffer_capacity;
> - QemuThread thread;
> -} QEMUFileBuffered;
> -
> -#ifdef DEBUG_BUFFERED_FILE
> -#define DPRINTF(fmt, ...) \
> - do { printf("buffered-file: " fmt, ## __VA_ARGS__); } while (0)
> -#else
> -#define DPRINTF(fmt, ...) \
> - do { } while (0)
> -#endif
> -
> -static ssize_t buffered_flush(QEMUFileBuffered *s)
> -{
> - size_t offset = 0;
> - ssize_t ret = 0;
> -
> - DPRINTF("flushing %zu byte(s) of data\n", s->buffer_size);
> -
> - while (s->bytes_xfer < s->xfer_limit && offset < s->buffer_size) {
> -
> - ret = migrate_fd_put_buffer(s->migration_state, s->buffer + offset,
> - s->buffer_size - offset);
> - if (ret <= 0) {
> - DPRINTF("error flushing data, %zd\n", ret);
> - break;
> - } else {
> - DPRINTF("flushed %zd byte(s)\n", ret);
> - offset += ret;
> - s->bytes_xfer += ret;
> - }
> - }
> -
> - DPRINTF("flushed %zu of %zu byte(s)\n", offset, s->buffer_size);
> - memmove(s->buffer, s->buffer + offset, s->buffer_size - offset);
> - s->buffer_size -= offset;
> -
> - if (ret < 0) {
> - return ret;
> - }
> - return offset;
> -}
> -
> -static int buffered_put_buffer(void *opaque, const uint8_t *buf, int64_t pos, int size)
> -{
> - QEMUFileBuffered *s = opaque;
> - ssize_t error;
> -
> - DPRINTF("putting %d bytes at %" PRId64 "\n", size, pos);
> -
> - error = qemu_file_get_error(s->file);
> - if (error) {
> - DPRINTF("flush when error, bailing: %s\n", strerror(-error));
> - return error;
> - }
> -
> - if (size <= 0) {
> - return size;
> - }
> -
> - if (size > (s->buffer_capacity - s->buffer_size)) {
> - DPRINTF("increasing buffer capacity from %zu by %zu\n",
> - s->buffer_capacity, size + 1024);
> -
> - s->buffer_capacity += size + 1024;
> -
> - s->buffer = g_realloc(s->buffer, s->buffer_capacity);
> - }
> -
> - memcpy(s->buffer + s->buffer_size, buf, size);
> - s->buffer_size += size;
> -
> - return size;
> -}
> -
> -static int buffered_close(void *opaque)
> -{
> - QEMUFileBuffered *s = opaque;
> - ssize_t ret = 0;
> - int ret2;
> -
> - DPRINTF("closing\n");
> -
> - s->xfer_limit = INT_MAX;
> - while (!qemu_file_get_error(s->file) && s->buffer_size) {
> - ret = buffered_flush(s);
> - if (ret < 0) {
> - break;
> - }
> - }
> -
> - ret2 = migrate_fd_close(s->migration_state);
> - if (ret >= 0) {
> - ret = ret2;
> - }
> - ret = migrate_fd_close(s->migration_state);
> - s->migration_state->complete = true;
> - return ret;
> -}
> -
> -/*
> - * The meaning of the return values is:
> - * 0: We can continue sending
> - * 1: Time to stop
> - * negative: There has been an error
> - */
> -static int buffered_rate_limit(void *opaque)
> -{
> - QEMUFileBuffered *s = opaque;
> - int ret;
> -
> - ret = qemu_file_get_error(s->file);
> - if (ret) {
> - return ret;
> - }
> -
> - if (s->bytes_xfer > s->xfer_limit)
> - return 1;
> -
> - return 0;
> -}
> -
> -static int64_t buffered_set_rate_limit(void *opaque, int64_t new_rate)
> -{
> - QEMUFileBuffered *s = opaque;
> - if (qemu_file_get_error(s->file)) {
> - goto out;
> - }
> - if (new_rate > SIZE_MAX) {
> - new_rate = SIZE_MAX;
> - }
> -
> - s->xfer_limit = new_rate / 10;
> -
> -out:
> - return s->xfer_limit;
> -}
> -
> -static int64_t buffered_get_rate_limit(void *opaque)
> -{
> - QEMUFileBuffered *s = opaque;
> -
> - return s->xfer_limit;
> -}
> -
> -/* 100ms xfer_limit is the limit that we should write each 100ms */
> -#define BUFFER_DELAY 100
> -
> -static void *buffered_file_thread(void *opaque)
> -{
> - QEMUFileBuffered *s = opaque;
> - int64_t initial_time = qemu_get_clock_ms(rt_clock);
> - int64_t max_size = 0;
> - bool last_round = false;
> -
> - while (true) {
> - int64_t current_time = qemu_get_clock_ms(rt_clock);
> -
> - if (s->migration_state->complete) {
> - break;
> - }
> - if (current_time >= initial_time + BUFFER_DELAY) {
> - uint64_t transferred_bytes = s->bytes_xfer;
> - uint64_t time_spent = current_time - initial_time;
> - double bandwidth = transferred_bytes / time_spent;
> - max_size = bandwidth * migrate_max_downtime() / 1000000;
> -
> - DPRINTF("transferred %" PRIu64 " time_spent %" PRIu64
> - " bandwidth %g max_size %" PRId64 "\n",
> - transferred_bytes, time_spent, bandwidth, max_size);
> -
> - s->bytes_xfer = 0;
> - initial_time = current_time;
> - }
> - if (!last_round && (s->bytes_xfer >= s->xfer_limit)) {
> - /* usleep expects microseconds */
> - usleep((initial_time + BUFFER_DELAY - current_time)*1000);
> - }
> - buffered_flush(s);
> -
> - DPRINTF("file is ready\n");
> - if (s->bytes_xfer < s->xfer_limit) {
> - DPRINTF("notifying client\n");
> - last_round = migrate_fd_put_ready(s->migration_state, max_size);
> - }
> - }
> -
> - g_free(s->buffer);
> - g_free(s);
> - return NULL;
> -}
> -
> -void qemu_fopen_ops_buffered(MigrationState *migration_state)
> -{
> - QEMUFileBuffered *s;
> -
> - s = g_malloc0(sizeof(*s));
> -
> - s->migration_state = migration_state;
> - s->xfer_limit = migration_state->bandwidth_limit / 10;
> - s->migration_state->complete = false;
> -
> - s->file = qemu_fopen_ops(s, buffered_put_buffer, NULL,
> - buffered_close, buffered_rate_limit,
> - buffered_set_rate_limit,
> - buffered_get_rate_limit);
> -
> - migration_state->file = s->file;
> -
> - qemu_thread_create(&s->thread, buffered_file_thread, s,
> - QEMU_THREAD_DETACHED);
> -}
> diff --git a/buffered_file.h b/buffered_file.h
> deleted file mode 100644
> index 8a246fd..0000000
> --- a/buffered_file.h
> +++ /dev/null
> @@ -1,22 +0,0 @@
> -/*
> - * QEMU buffered QEMUFile
> - *
> - * Copyright IBM, Corp. 2008
> - *
> - * Authors:
> - * Anthony Liguori <aliguori@us.ibm.com>
> - *
> - * This work is licensed under the terms of the GNU GPL, version 2. See
> - * the COPYING file in the top-level directory.
> - *
> - */
> -
> -#ifndef QEMU_BUFFERED_FILE_H
> -#define QEMU_BUFFERED_FILE_H
> -
> -#include "hw/hw.h"
> -#include "migration.h"
> -
> -void qemu_fopen_ops_buffered(MigrationState *migration_state);
> -
> -#endif
> diff --git a/migration.c b/migration.c
> index 8054a77..b1567f3 100644
> --- a/migration.c
> +++ b/migration.c
> @@ -16,7 +16,7 @@
> #include "qemu-common.h"
> #include "migration.h"
> #include "monitor.h"
> -#include "buffered_file.h"
> +#include "qemu-file.h"
> #include "sysemu.h"
> #include "block.h"
> #include "qemu_socket.h"
> @@ -569,3 +569,219 @@ int64_t migrate_xbzrle_cache_size(void)
>
> return s->xbzrle_cache_size;
> }
> +
> +/* migration thread support */
> +
> +typedef struct QEMUFileBuffered {
> + MigrationState *migration_state;
> + QEMUFile *file;
> + size_t bytes_xfer;
> + size_t xfer_limit;
> + uint8_t *buffer;
> + size_t buffer_size;
> + size_t buffer_capacity;
> + QemuThread thread;
> +} QEMUFileBuffered;
> +
> +static ssize_t buffered_flush(QEMUFileBuffered *s)
> +{
> + size_t offset = 0;
> + ssize_t ret = 0;
> +
> + DPRINTF("flushing %zu byte(s) of data\n", s->buffer_size);
> +
> + while (s->bytes_xfer < s->xfer_limit && offset < s->buffer_size) {
> +
> + ret = migrate_fd_put_buffer(s->migration_state, s->buffer + offset,
> + s->buffer_size - offset);
> + if (ret <= 0) {
> + DPRINTF("error flushing data, %zd\n", ret);
> + break;
> + } else {
> + DPRINTF("flushed %zd byte(s)\n", ret);
> + offset += ret;
> + s->bytes_xfer += ret;
> + }
> + }
> +
> + DPRINTF("flushed %zu of %zu byte(s)\n", offset, s->buffer_size);
> + memmove(s->buffer, s->buffer + offset, s->buffer_size - offset);
> + s->buffer_size -= offset;
> +
> + if (ret < 0) {
> + return ret;
> + }
> + return offset;
> +}
> +
> +static int buffered_put_buffer(void *opaque, const uint8_t *buf,
> + int64_t pos, int size)
> +{
> + QEMUFileBuffered *s = opaque;
> + ssize_t error;
> +
> + DPRINTF("putting %d bytes at %" PRId64 "\n", size, pos);
> +
> + error = qemu_file_get_error(s->file);
> + if (error) {
> + DPRINTF("flush when error, bailing: %s\n", strerror(-error));
> + return error;
> + }
> +
> + if (size <= 0) {
> + return size;
> + }
> +
> + if (size > (s->buffer_capacity - s->buffer_size)) {
> + DPRINTF("increasing buffer capacity from %zu by %zu\n",
> + s->buffer_capacity, size + 1024);
> +
> + s->buffer_capacity += size + 1024;
> +
> + s->buffer = g_realloc(s->buffer, s->buffer_capacity);
> + }
> +
> + memcpy(s->buffer + s->buffer_size, buf, size);
> + s->buffer_size += size;
> +
> + return size;
> +}
> +
> +static int buffered_close(void *opaque)
> +{
> + QEMUFileBuffered *s = opaque;
> + ssize_t ret = 0;
> + int ret2;
> +
> + DPRINTF("closing\n");
> +
> + s->xfer_limit = INT_MAX;
> + while (!qemu_file_get_error(s->file) && s->buffer_size) {
> + ret = buffered_flush(s);
> + if (ret < 0) {
> + break;
> + }
> + }
> +
> + ret2 = migrate_fd_close(s->migration_state);
> + if (ret >= 0) {
> + ret = ret2;
> + }
> + ret = migrate_fd_close(s->migration_state);
> + s->migration_state->complete = true;
> + return ret;
> +}
> +
> +/*
> + * The meaning of the return values is:
> + * 0: We can continue sending
> + * 1: Time to stop
> + * negative: There has been an error
> + */
> +static int buffered_rate_limit(void *opaque)
> +{
> + QEMUFileBuffered *s = opaque;
> + int ret;
> +
> + ret = qemu_file_get_error(s->file);
> + if (ret) {
> + return ret;
> + }
> +
> + if (s->bytes_xfer > s->xfer_limit) {
> + return 1;
> + }
> +
> + return 0;
> +}
> +
> +static int64_t buffered_set_rate_limit(void *opaque, int64_t new_rate)
> +{
> + QEMUFileBuffered *s = opaque;
> + if (qemu_file_get_error(s->file)) {
> + goto out;
> + }
> + if (new_rate > SIZE_MAX) {
> + new_rate = SIZE_MAX;
> + }
> +
> + s->xfer_limit = new_rate / 10;
> +
> +out:
> + return s->xfer_limit;
> +}
> +
> +static int64_t buffered_get_rate_limit(void *opaque)
> +{
> + QEMUFileBuffered *s = opaque;
> +
> + return s->xfer_limit;
> +}
> +
> +/* 100ms xfer_limit is the limit that we should write each 100ms */
> +#define BUFFER_DELAY 100
> +
> +static void *buffered_file_thread(void *opaque)
> +{
> + QEMUFileBuffered *s = opaque;
> + int64_t initial_time = qemu_get_clock_ms(rt_clock);
> + int64_t max_size = 0;
> + bool last_round = false;
> +
> + while (true) {
> + int64_t current_time = qemu_get_clock_ms(rt_clock);
> +
> + if (s->migration_state->complete) {
> + break;
> + }
> + if (current_time >= initial_time + BUFFER_DELAY) {
> + uint64_t transferred_bytes = s->bytes_xfer;
> + uint64_t time_spent = current_time - initial_time;
> + double bandwidth = transferred_bytes / time_spent;
> + max_size = bandwidth * migrate_max_downtime() / 1000000;
> +
> + DPRINTF("transferred %" PRIu64 " time_spent %" PRIu64
> + " bandwidth %g max_size %" PRId64 "\n",
> + transferred_bytes, time_spent, bandwidth, max_size);
> +
> + s->bytes_xfer = 0;
> + initial_time = current_time;
> + }
> + if (!last_round && (s->bytes_xfer >= s->xfer_limit)) {
> + /* usleep expects microseconds */
> + usleep((initial_time + BUFFER_DELAY - current_time)*1000);
> + }
> + buffered_flush(s);
> +
> + DPRINTF("file is ready\n");
> + if (s->bytes_xfer < s->xfer_limit) {
> + DPRINTF("notifying client\n");
> + last_round = migrate_fd_put_ready(s->migration_state, max_size);
> + }
> + }
> +
> + g_free(s->buffer);
> + g_free(s);
> + return NULL;
> +}
> +
> +void qemu_fopen_ops_buffered(MigrationState *migration_state)
> +{
> + QEMUFileBuffered *s;
> +
> + s = g_malloc0(sizeof(*s));
> +
> + s->migration_state = migration_state;
> + s->xfer_limit = migration_state->bandwidth_limit / 10;
> + s->migration_state->complete = false;
> +
> + s->file = qemu_fopen_ops(s, buffered_put_buffer, NULL,
> + buffered_close, buffered_rate_limit,
> + buffered_set_rate_limit,
> + buffered_get_rate_limit);
> +
> + migration_state->file = s->file;
> +
> + qemu_thread_create(&s->thread, buffered_file_thread, s,
> + QEMU_THREAD_DETACHED);
> +}
> diff --git a/migration.h b/migration.h
> index 1f2baed..40c8e9c 100644
> --- a/migration.h
> +++ b/migration.h
> @@ -129,4 +129,5 @@ int64_t migrate_xbzrle_cache_size(void);
>
> int64_t xbzrle_cache_resize(int64_t new_size);
>
> +void qemu_fopen_ops_buffered(MigrationState *migration_state);
> #endif
>
next prev parent reply other threads:[~2012-10-18 8:57 UTC|newest]
Thread overview: 58+ messages / expand[flat|nested] mbox.gz Atom feed top
2012-10-18 7:29 [Qemu-devel] [PATCH 00/30] Migration thread 20121017 edition Juan Quintela
2012-10-18 7:29 ` [Qemu-devel] [PATCH 01/30] split MRU ram list Juan Quintela
2012-10-21 11:58 ` Orit Wasserman
2012-10-21 17:02 ` Peter Maydell
2012-10-18 7:29 ` [Qemu-devel] [PATCH 02/30] add a version number to ram_list Juan Quintela
2012-10-21 12:00 ` Orit Wasserman
2012-10-18 7:29 ` [Qemu-devel] [PATCH 03/30] protect the ramlist with a separate mutex Juan Quintela
2012-10-21 12:05 ` Orit Wasserman
2012-10-18 7:30 ` [Qemu-devel] [PATCH 04/30] buffered_file: Move from using a timer to use a thread Juan Quintela
2012-10-18 8:56 ` Paolo Bonzini
2012-10-21 12:09 ` Orit Wasserman
2012-11-12 11:42 ` Paolo Bonzini
2012-10-18 7:30 ` [Qemu-devel] [PATCH 05/30] migration: make qemu_fopen_ops_buffered() return void Juan Quintela
2012-10-21 12:10 ` Orit Wasserman
2012-10-18 7:30 ` [Qemu-devel] [PATCH 06/30] migration: stop all cpus correctly Juan Quintela
2012-11-12 11:44 ` Paolo Bonzini
2012-11-14 15:21 ` Paolo Bonzini
2012-12-14 12:36 ` Juan Quintela
2012-12-14 13:53 ` Paolo Bonzini
2012-10-18 7:30 ` [Qemu-devel] [PATCH 07/30] migration: make writes blocking Juan Quintela
2012-11-12 11:52 ` Paolo Bonzini
2012-10-18 7:30 ` [Qemu-devel] [PATCH 08/30] migration: remove unfreeze logic Juan Quintela
2012-10-18 7:30 ` [Qemu-devel] [PATCH 09/30] migration: take finer locking Juan Quintela
2012-10-18 7:30 ` [Qemu-devel] [PATCH 10/30] buffered_file: Unfold the trick to restart generating migration data Juan Quintela
2012-10-18 7:30 ` [Qemu-devel] [PATCH 11/30] buffered_file: don't flush on put buffer Juan Quintela
2012-10-18 7:30 ` [Qemu-devel] [PATCH 12/30] buffered_file: unfold buffered_append in buffered_put_buffer Juan Quintela
2012-10-18 7:30 ` [Qemu-devel] [PATCH 13/30] savevm: New save live migration method: pending Juan Quintela
2012-10-18 7:30 ` [Qemu-devel] [PATCH 14/30] migration: include qemu-file.h Juan Quintela
2012-10-18 7:30 ` [Qemu-devel] [PATCH 15/30] migration-fd: remove duplicate include Juan Quintela
2012-10-18 7:30 ` [Qemu-devel] [PATCH 16/30] migration: move buffered_file.c code into migration.c Juan Quintela
2012-10-18 8:57 ` Paolo Bonzini [this message]
2012-10-18 7:30 ` [Qemu-devel] [PATCH 17/30] migration: move migration_fd_put_ready() Juan Quintela
2012-10-18 7:30 ` [Qemu-devel] [PATCH 18/30] migration: Inline qemu_fopen_ops_buffered into migrate_fd_connect Juan Quintela
2012-10-18 7:30 ` [Qemu-devel] [PATCH 19/30] migration: move migration notifier Juan Quintela
2012-10-18 7:30 ` [Qemu-devel] [PATCH 20/30] migration: move begining stage to the migration thread Juan Quintela
2012-10-18 7:30 ` [Qemu-devel] [PATCH 21/30] migration: move exit condition to " Juan Quintela
2012-10-18 8:34 ` Paolo Bonzini
2012-10-26 11:43 ` Juan Quintela
2012-10-18 7:30 ` [Qemu-devel] [PATCH 22/30] migration: unfold rest of migrate_fd_put_ready() into thread Juan Quintela
2012-10-18 8:39 ` Paolo Bonzini
2012-10-18 8:55 ` Paolo Bonzini
2012-10-18 7:30 ` [Qemu-devel] [PATCH 23/30] migration: print times for end phase Juan Quintela
2012-10-18 7:30 ` [Qemu-devel] [PATCH 24/30] ram: rename last_block to last_seen_block Juan Quintela
2012-10-18 7:30 ` [Qemu-devel] [PATCH 25/30] ram: Add last_sent_block Juan Quintela
2012-10-18 7:30 ` [Qemu-devel] [PATCH 26/30] memory: introduce memory_region_test_and_clear_dirty Juan Quintela
2012-10-18 7:30 ` [Qemu-devel] [PATCH 27/30] ram: Use memory_region_test_and_clear_dirty Juan Quintela
2012-10-18 12:52 ` Eric Blake
2012-10-18 7:30 ` [Qemu-devel] [PATCH 28/30] fix memory.c Juan Quintela
2012-10-18 8:43 ` Paolo Bonzini
2012-10-18 7:30 ` [Qemu-devel] [PATCH 29/30] migration: Only go to the iterate stage if there is anything to send Juan Quintela
2012-10-18 7:30 ` [Qemu-devel] [PATCH 30/30] ram: optimize migration bitmap walking Juan Quintela
2012-10-21 13:01 ` Orit Wasserman
2012-10-26 11:39 ` Juan Quintela
2012-10-28 8:35 ` Orit Wasserman
2012-10-30 10:15 ` Orit Wasserman
2012-10-30 15:33 ` Juan Quintela
2012-10-18 9:00 ` [Qemu-devel] [PATCH 00/30] Migration thread 20121017 edition Paolo Bonzini
2012-10-26 13:04 ` Paolo Bonzini
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=507FC472.9020700@redhat.com \
--to=pbonzini@redhat.com \
--cc=qemu-devel@nongnu.org \
--cc=quintela@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.