mirror of
https://github.com/checkpoint-restore/criu
synced 2025-08-22 01:51:51 +00:00
We want to have buffered images to speed up dump and, slightly, restore. Right now we use plan file descriptors to write and read images to/from. Making them buffered cannot be gracefully done on plain fds, so introduce a new class. This will also help if (when?) we will want to do more complex changes with images, e.g. store them all in one file or send them directly to the network. For now the cr_img just contains one int _fd variable. This patch chages the prototype of open_image() to return struct cr_img *, pb_(read|write)* to accept one and fixes the compilation of the rest of the code :) Signed-off-by: Pavel Emelyanov <xemul@parallels.com> Acked-by: Cyrill Gorcunov <gorcunov@openvz.org>
257 lines
5.1 KiB
C
257 lines
5.1 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <limits.h>
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/sendfile.h>
|
|
|
|
#include "asm/types.h"
|
|
#include "list.h"
|
|
#include "imgset.h"
|
|
#include "image.h"
|
|
#include "servicefd.h"
|
|
#include "cr_options.h"
|
|
#include "util.h"
|
|
#include "util-pie.h"
|
|
#include "sockets.h"
|
|
|
|
#include "sk-queue.h"
|
|
|
|
#include "protobuf.h"
|
|
#include "protobuf/sk-packet.pb-c.h"
|
|
|
|
struct sk_packet {
|
|
struct list_head list;
|
|
SkPacketEntry *entry;
|
|
off_t img_off;
|
|
};
|
|
|
|
static LIST_HEAD(packets_list);
|
|
|
|
int read_sk_queues(void)
|
|
{
|
|
struct sk_packet *pkt;
|
|
int ret;
|
|
struct cr_img *img;
|
|
|
|
pr_info("Trying to read socket queues image\n");
|
|
|
|
img = open_image(CR_FD_SK_QUEUES, O_RSTR);
|
|
if (!img)
|
|
return -1;
|
|
|
|
while (1) {
|
|
ret = -1;
|
|
pkt = xmalloc(sizeof(*pkt));
|
|
if (!pkt) {
|
|
pr_err("Failed to allocate packet header\n");
|
|
break;
|
|
}
|
|
ret = pb_read_one_eof(img, &pkt->entry, PB_SK_QUEUES);
|
|
if (ret <= 0)
|
|
break;
|
|
|
|
pkt->img_off = lseek(img_raw_fd(img), 0, SEEK_CUR);
|
|
/*
|
|
* NOTE: packet must be added to the tail. Otherwise sequence
|
|
* will be broken.
|
|
*/
|
|
list_add_tail(&pkt->list, &packets_list);
|
|
lseek(img_raw_fd(img), pkt->entry->length, SEEK_CUR);
|
|
}
|
|
close_image(img);
|
|
xfree(pkt);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int dump_sk_queue(int sock_fd, int sock_id)
|
|
{
|
|
SkPacketEntry pe = SK_PACKET_ENTRY__INIT;
|
|
int ret, size, orig_peek_off;
|
|
void *data;
|
|
socklen_t tmp;
|
|
|
|
/*
|
|
* Save original peek offset.
|
|
*/
|
|
tmp = sizeof(orig_peek_off);
|
|
orig_peek_off = 0;
|
|
ret = getsockopt(sock_fd, SOL_SOCKET, SO_PEEK_OFF, &orig_peek_off, &tmp);
|
|
if (ret < 0) {
|
|
pr_perror("getsockopt failed");
|
|
return ret;
|
|
}
|
|
/*
|
|
* Discover max DGRAM size
|
|
*/
|
|
tmp = sizeof(size);
|
|
size = 0;
|
|
ret = getsockopt(sock_fd, SOL_SOCKET, SO_SNDBUF, &size, &tmp);
|
|
if (ret < 0) {
|
|
pr_perror("getsockopt failed");
|
|
return ret;
|
|
}
|
|
|
|
/* Note: 32 bytes will be used by kernel for protocol header. */
|
|
size -= 32;
|
|
|
|
/*
|
|
* Allocate data for a stream.
|
|
*/
|
|
data = xmalloc(size);
|
|
if (!data)
|
|
return -1;
|
|
|
|
/*
|
|
* Enable peek offset incrementation.
|
|
*/
|
|
ret = setsockopt(sock_fd, SOL_SOCKET, SO_PEEK_OFF, &ret, sizeof(int));
|
|
if (ret < 0) {
|
|
pr_perror("setsockopt fail");
|
|
goto err_brk;
|
|
}
|
|
|
|
pe.id_for = sock_id;
|
|
|
|
while (1) {
|
|
struct iovec iov = {
|
|
.iov_base = data,
|
|
.iov_len = size,
|
|
};
|
|
struct msghdr msg = {
|
|
.msg_iov = &iov,
|
|
.msg_iovlen = 1,
|
|
};
|
|
|
|
ret = pe.length = recvmsg(sock_fd, &msg, MSG_DONTWAIT | MSG_PEEK);
|
|
if (!ret)
|
|
/*
|
|
* It means, that peer has performed an
|
|
* orderly shutdown, so we're done.
|
|
*/
|
|
break;
|
|
else if (ret < 0) {
|
|
if (errno == EAGAIN)
|
|
break; /* we're done */
|
|
pr_perror("recvmsg fail: error");
|
|
goto err_set_sock;
|
|
}
|
|
if (msg.msg_flags & MSG_TRUNC) {
|
|
/*
|
|
* DGRAM truncated. This should not happen. But we have
|
|
* to check...
|
|
*/
|
|
pr_err("sys_recvmsg failed: truncated\n");
|
|
ret = -E2BIG;
|
|
goto err_set_sock;
|
|
}
|
|
|
|
ret = pb_write_one(img_from_set(glob_imgset, CR_FD_SK_QUEUES), &pe, PB_SK_QUEUES);
|
|
if (ret < 0) {
|
|
ret = -EIO;
|
|
goto err_set_sock;
|
|
}
|
|
|
|
ret = write_img_buf(img_from_set(glob_imgset, CR_FD_SK_QUEUES), data, pe.length);
|
|
if (ret < 0) {
|
|
ret = -EIO;
|
|
goto err_set_sock;
|
|
}
|
|
}
|
|
ret = 0;
|
|
|
|
err_set_sock:
|
|
/*
|
|
* Restore original peek offset.
|
|
*/
|
|
if (setsockopt(sock_fd, SOL_SOCKET, SO_PEEK_OFF, &orig_peek_off, sizeof(int))) {
|
|
pr_perror("setsockopt failed on restore");
|
|
ret = -1;
|
|
}
|
|
err_brk:
|
|
xfree(data);
|
|
return ret;
|
|
}
|
|
|
|
void sk_queue_data_handler(struct cr_img *img, void *obj)
|
|
{
|
|
SkPacketEntry *e = obj;
|
|
print_image_data(img, e->length, opts.show_pages_content);
|
|
}
|
|
|
|
int restore_sk_queue(int fd, unsigned int peer_id)
|
|
{
|
|
struct sk_packet *pkt, *tmp;
|
|
int ret;
|
|
struct cr_img *img;
|
|
|
|
pr_info("Trying to restore recv queue for %u\n", peer_id);
|
|
|
|
if (restore_prepare_socket(fd))
|
|
return -1;
|
|
|
|
img = open_image(CR_FD_SK_QUEUES, O_RSTR);
|
|
if (!img)
|
|
return -1;
|
|
|
|
list_for_each_entry_safe(pkt, tmp, &packets_list, list) {
|
|
SkPacketEntry *entry = pkt->entry;
|
|
char *buf;
|
|
|
|
if (entry->id_for != peer_id)
|
|
continue;
|
|
|
|
pr_info("\tRestoring %d-bytes skb for %u\n",
|
|
(unsigned int)entry->length, peer_id);
|
|
|
|
/*
|
|
* Don't try to use sendfile here, because it use sendpage() and
|
|
* all data are split on pages and a new skb is allocated for
|
|
* each page. It creates a big overhead on SNDBUF.
|
|
* sendfile() isn't suitable for DGRAM sockets, because message
|
|
* boundaries messages should be saved.
|
|
*/
|
|
|
|
buf = xmalloc(entry->length);
|
|
if (buf ==NULL)
|
|
goto err;
|
|
|
|
if (lseek(img_raw_fd(img), pkt->img_off, SEEK_SET) == -1) {
|
|
pr_perror("lseek() failed");
|
|
xfree(buf);
|
|
goto err;
|
|
}
|
|
if (read_img_buf(img, buf, entry->length) != 1) {
|
|
xfree(buf);
|
|
goto err;
|
|
}
|
|
|
|
ret = write(fd, buf, entry->length);
|
|
xfree(buf);
|
|
if (ret < 0) {
|
|
pr_perror("Failed to send packet");
|
|
goto err;
|
|
}
|
|
if (ret != entry->length) {
|
|
pr_err("Restored skb trimmed to %d/%d\n",
|
|
ret, (unsigned int)entry->length);
|
|
goto err;
|
|
}
|
|
list_del(&pkt->list);
|
|
sk_packet_entry__free_unpacked(entry, NULL);
|
|
xfree(pkt);
|
|
}
|
|
|
|
close_image(img);
|
|
return 0;
|
|
err:
|
|
close_image(img);
|
|
return -1;
|
|
}
|