mirror of
https://github.com/checkpoint-restore/criu.git
synced 2026-07-23 01:59:04 +00:00
bfd: Support dynamic buffer resizing
Currently, bfd has a fixed buffer size (BUFSIZE, which is 4096). This causes issues when reading lines longer than BUFSIZE, as breadline fails with "The bfd buffer is too small". This patch introduces dynamic buffer resizing in bfd. When a buffer is full and more space is needed (e.g., for a very long line), the buffer is resized using mremap (or a new mmap if it was using a pre-allocated buffer from the pool). A new bsize field is added to struct xbuf to keep track of the current buffer size. Unit tests for reading long lines and writing large buffers are added to criu/unittest/unit.c. Signed-off-by: dong sunchao <dongsunchao@gmail.com> Co-developed-by: Andrei Vagin <avagin@google.com> Signed-off-by: Andrei Vagin <avagin@google.com>
This commit is contained in:
parent
2c351c73d7
commit
b99cbc1e8c
4 changed files with 146 additions and 11 deletions
|
|
@ -88,6 +88,7 @@ $(obj)/criu: $(PROGRAM-BUILTINS)
|
|||
$(Q) $(CC) $(CFLAGS) $^ $(LDFLAGS) $(LIBS) $(WRAPFLAGS) $(GMONLDOPT) -rdynamic -o $@
|
||||
|
||||
UNIT-BUILTINS += $(obj)/util.o
|
||||
UNIT-BUILTINS += $(obj)/bfd.o
|
||||
UNIT-BUILTINS += $(obj)/config.o
|
||||
UNIT-BUILTINS += $(obj)/log.o
|
||||
UNIT-BUILTINS += $(obj)/string.o
|
||||
|
|
|
|||
68
criu/bfd.c
68
criu/bfd.c
|
|
@ -25,6 +25,8 @@
|
|||
*/
|
||||
#define BUFSIZE (PAGE_SIZE)
|
||||
|
||||
#define BFD_MAX_DYNAMIC_SIZE (2 * 1024 * 1024)
|
||||
|
||||
struct bfd_buf {
|
||||
char *mem;
|
||||
struct list_head l;
|
||||
|
|
@ -70,6 +72,7 @@ static int buf_get(struct xbuf *xb)
|
|||
|
||||
xb->mem = b->mem;
|
||||
xb->data = xb->mem;
|
||||
xb->bsize = BUFSIZE;
|
||||
xb->sz = 0;
|
||||
xb->buf = b;
|
||||
return 0;
|
||||
|
|
@ -77,11 +80,16 @@ static int buf_get(struct xbuf *xb)
|
|||
|
||||
static void buf_put(struct xbuf *xb)
|
||||
{
|
||||
/*
|
||||
* Don't unmap buffer back, it will get reused
|
||||
* by next bfdopen call
|
||||
*/
|
||||
list_add(&xb->buf->l, &bufs);
|
||||
if (xb->buf) {
|
||||
/*
|
||||
* Don't unmap standard buffer back, it will get reused
|
||||
* by next bfdopen call
|
||||
*/
|
||||
list_add(&xb->buf->l, &bufs);
|
||||
} else {
|
||||
/* This buffer was dynamically extended, unmap it */
|
||||
munmap(xb->mem, xb->bsize);
|
||||
}
|
||||
xb->buf = NULL;
|
||||
xb->mem = NULL;
|
||||
xb->data = NULL;
|
||||
|
|
@ -144,7 +152,7 @@ static int brefill(struct bfd *f)
|
|||
memmove(b->mem, b->data, b->sz);
|
||||
b->data = b->mem;
|
||||
|
||||
ret = read_all(f->fd, b->mem + b->sz, BUFSIZE - b->sz);
|
||||
ret = read_all(f->fd, b->mem + b->sz, b->bsize - b->sz);
|
||||
if (ret < 0) {
|
||||
pr_perror("Error reading file");
|
||||
return -1;
|
||||
|
|
@ -172,6 +180,40 @@ char *breadline(struct bfd *f)
|
|||
return breadchr(f, '\n');
|
||||
}
|
||||
|
||||
static int bextend(struct bfd *f)
|
||||
{
|
||||
struct xbuf *b = &f->b;
|
||||
void *newbuf;
|
||||
long newsize = b->bsize * 2;
|
||||
|
||||
if (newsize > BFD_MAX_DYNAMIC_SIZE) {
|
||||
pr_err("Line too long to fit in BFD_MAX_DYNAMIC_SIZE\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (b->buf) {
|
||||
newbuf = mmap(NULL, newsize, PROT_READ | PROT_WRITE,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
|
||||
if (newbuf == MAP_FAILED) {
|
||||
pr_perror("Error allocating buffer");
|
||||
return -1;
|
||||
}
|
||||
memcpy(newbuf, b->mem, b->sz);
|
||||
list_add(&b->buf->l, &bufs);
|
||||
b->buf = NULL;
|
||||
} else {
|
||||
newbuf = mremap(b->mem, b->bsize, newsize, MREMAP_MAYMOVE);
|
||||
if (newbuf == MAP_FAILED) {
|
||||
pr_perror("Error allocating buffer");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
b->mem = newbuf;
|
||||
b->data = newbuf;
|
||||
b->bsize = newsize;
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *breadchr(struct bfd *f, char c)
|
||||
{
|
||||
struct xbuf *b = &f->b;
|
||||
|
|
@ -195,9 +237,12 @@ again:
|
|||
if (!b->sz)
|
||||
return NULL;
|
||||
|
||||
if (b->sz == BUFSIZE) {
|
||||
pr_err("The bfd buffer is too small\n");
|
||||
return ERR_PTR(-EIO);
|
||||
if (b->sz == b->bsize) {
|
||||
if (bextend(f)) {
|
||||
pr_err("The bfd buffer is too small\n");
|
||||
return ERR_PTR(-EIO);
|
||||
}
|
||||
goto refill;
|
||||
}
|
||||
/*
|
||||
* Last bytes may lack the \n at the
|
||||
|
|
@ -216,6 +261,7 @@ again:
|
|||
return b->data;
|
||||
}
|
||||
|
||||
refill:
|
||||
/*
|
||||
* small optimization -- we've scanned b->sz
|
||||
* symbols already, no need to re-scan them after
|
||||
|
|
@ -252,14 +298,14 @@ static int __bwrite(struct bfd *bfd, const void *buf, int size)
|
|||
{
|
||||
struct xbuf *b = &bfd->b;
|
||||
|
||||
if (b->sz + size > BUFSIZE) {
|
||||
if (b->sz + size > b->bsize) {
|
||||
int ret;
|
||||
ret = bflush(bfd);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (size > BUFSIZE)
|
||||
if (size > b->bsize)
|
||||
return write_all(bfd->fd, buf, size);
|
||||
|
||||
memcpy(b->data + b->sz, buf, size);
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ struct xbuf {
|
|||
char *mem; /* buffer */
|
||||
char *data; /* position we see bytes at */
|
||||
unsigned int sz; /* bytes sitting after b->pos */
|
||||
unsigned int bsize;
|
||||
struct bfd_buf *buf;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,97 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "util.h"
|
||||
#include "criu-log.h"
|
||||
#include "bfd.h"
|
||||
|
||||
int parse_statement(int i, char *line, char **configuration);
|
||||
|
||||
static void test_bfd(void)
|
||||
{
|
||||
struct bfd f;
|
||||
char *str;
|
||||
const int lines = 5;
|
||||
char *long_line[lines];
|
||||
int size = 1024 * 1024;
|
||||
int i, fd;
|
||||
|
||||
fd = memfd_create("criu-bfd-test", 0);
|
||||
assert(fd >= 0);
|
||||
|
||||
for (i = 0; i < lines; i++) {
|
||||
int j;
|
||||
|
||||
long_line[i] = malloc(size + 2);
|
||||
assert(long_line[i]);
|
||||
long_line[i][0] = 'A' + (i % 26);
|
||||
for (j = 1; j < size; j++)
|
||||
long_line[i][j] = 'a' + (j % 26);
|
||||
long_line[i][size] = '\n';
|
||||
long_line[i][size + 1] = '\0';
|
||||
|
||||
assert(write(fd, long_line[i], size + 1) == size + 1);
|
||||
}
|
||||
assert(lseek(fd, 0, SEEK_SET) == 0);
|
||||
|
||||
f.fd = fd;
|
||||
assert(bfdopenr(&f) == 0);
|
||||
|
||||
for (i = 0; i < lines; i++) {
|
||||
str = breadline(&f);
|
||||
assert(str);
|
||||
assert(strlen(str) == size);
|
||||
/* long_line has \n, str hasn't */
|
||||
assert(strcmp(str, long_line[i]) != 0);
|
||||
str[size] = '\n';
|
||||
assert(memcmp(str, long_line[i], size + 1) == 0);
|
||||
}
|
||||
|
||||
bclose(&f);
|
||||
for (i = 0; i < lines; i++)
|
||||
free(long_line[i]);
|
||||
}
|
||||
|
||||
static void test_bwrite(void)
|
||||
{
|
||||
struct bfd f;
|
||||
char *buf;
|
||||
int size = 1024 * 1024;
|
||||
int i;
|
||||
int fd;
|
||||
char *read_buf;
|
||||
|
||||
fd = memfd_create("criu-bfd-test", 0);
|
||||
assert(fd >= 0);
|
||||
|
||||
buf = malloc(size);
|
||||
assert(buf);
|
||||
for (i = 0; i < size; i++)
|
||||
buf[i] = 'z' - (i % 26);
|
||||
|
||||
f.fd = dup(fd);
|
||||
assert(f.fd >= 0);
|
||||
assert(bfdopenw(&f) == 0);
|
||||
|
||||
assert(bwrite(&f, buf, size) == size);
|
||||
bclose(&f);
|
||||
|
||||
assert(lseek(fd, 0, SEEK_SET) == 0);
|
||||
read_buf = malloc(size);
|
||||
assert(read_buf);
|
||||
assert(read(fd, read_buf, size) == size);
|
||||
assert(memcmp(buf, read_buf, size) == 0);
|
||||
|
||||
close(fd);
|
||||
free(buf);
|
||||
free(read_buf);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[], char *envp[])
|
||||
{
|
||||
char **configuration;
|
||||
|
|
@ -16,6 +100,9 @@ int main(int argc, char *argv[], char *envp[])
|
|||
configuration = malloc(10 * sizeof(char *));
|
||||
log_init(NULL);
|
||||
|
||||
test_bfd();
|
||||
test_bwrite();
|
||||
|
||||
i = parse_statement(0, "", configuration);
|
||||
assert(i == 0);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue