Skip to content

Commit 145308d

Browse files
DongSunchaoavagin
authored andcommitted
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>
1 parent cff99db commit 145308d

File tree

4 files changed

+146
-11
lines changed

4 files changed

+146
-11
lines changed

criu/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ $(obj)/criu: $(PROGRAM-BUILTINS)
8888
$(Q) $(CC) $(CFLAGS) $^ $(LDFLAGS) $(LIBS) $(WRAPFLAGS) $(GMONLDOPT) -rdynamic -o $@
8989

9090
UNIT-BUILTINS += $(obj)/util.o
91+
UNIT-BUILTINS += $(obj)/bfd.o
9192
UNIT-BUILTINS += $(obj)/config.o
9293
UNIT-BUILTINS += $(obj)/log.o
9394
UNIT-BUILTINS += $(obj)/string.o

criu/bfd.c

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
*/
2626
#define BUFSIZE (PAGE_SIZE)
2727

28+
#define BFD_MAX_DYNAMIC_SIZE (2 * 1024 * 1024)
29+
2830
struct bfd_buf {
2931
char *mem;
3032
struct list_head l;
@@ -70,18 +72,24 @@ static int buf_get(struct xbuf *xb)
7072

7173
xb->mem = b->mem;
7274
xb->data = xb->mem;
75+
xb->bsize = BUFSIZE;
7376
xb->sz = 0;
7477
xb->buf = b;
7578
return 0;
7679
}
7780

7881
static void buf_put(struct xbuf *xb)
7982
{
80-
/*
81-
* Don't unmap buffer back, it will get reused
82-
* by next bfdopen call
83-
*/
84-
list_add(&xb->buf->l, &bufs);
83+
if (xb->buf) {
84+
/*
85+
* Don't unmap standard buffer back, it will get reused
86+
* by next bfdopen call
87+
*/
88+
list_add(&xb->buf->l, &bufs);
89+
} else {
90+
/* This buffer was dynamically extended, unmap it */
91+
munmap(xb->mem, xb->bsize);
92+
}
8593
xb->buf = NULL;
8694
xb->mem = NULL;
8795
xb->data = NULL;
@@ -144,7 +152,7 @@ static int brefill(struct bfd *f)
144152
memmove(b->mem, b->data, b->sz);
145153
b->data = b->mem;
146154

147-
ret = read_all(f->fd, b->mem + b->sz, BUFSIZE - b->sz);
155+
ret = read_all(f->fd, b->mem + b->sz, b->bsize - b->sz);
148156
if (ret < 0) {
149157
pr_perror("Error reading file");
150158
return -1;
@@ -172,6 +180,40 @@ char *breadline(struct bfd *f)
172180
return breadchr(f, '\n');
173181
}
174182

183+
static int bextend(struct bfd *f)
184+
{
185+
struct xbuf *b = &f->b;
186+
void *newbuf;
187+
long newsize = b->bsize * 2;
188+
189+
if (newsize > BFD_MAX_DYNAMIC_SIZE) {
190+
pr_err("Line too long to fit in BFD_MAX_DYNAMIC_SIZE\n");
191+
return -1;
192+
}
193+
194+
if (b->buf) {
195+
newbuf = mmap(NULL, newsize, PROT_READ | PROT_WRITE,
196+
MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
197+
if (newbuf == MAP_FAILED) {
198+
pr_perror("Error allocating buffer");
199+
return -1;
200+
}
201+
memcpy(newbuf, b->mem, b->sz);
202+
list_add(&b->buf->l, &bufs);
203+
b->buf = NULL;
204+
} else {
205+
newbuf = mremap(b->mem, b->bsize, newsize, MREMAP_MAYMOVE);
206+
if (newbuf == MAP_FAILED) {
207+
pr_perror("Error allocating buffer");
208+
return -1;
209+
}
210+
}
211+
b->mem = newbuf;
212+
b->data = newbuf;
213+
b->bsize = newsize;
214+
return 0;
215+
}
216+
175217
char *breadchr(struct bfd *f, char c)
176218
{
177219
struct xbuf *b = &f->b;
@@ -195,9 +237,12 @@ char *breadchr(struct bfd *f, char c)
195237
if (!b->sz)
196238
return NULL;
197239

198-
if (b->sz == BUFSIZE) {
199-
pr_err("The bfd buffer is too small\n");
200-
return ERR_PTR(-EIO);
240+
if (b->sz == b->bsize) {
241+
if (bextend(f)) {
242+
pr_err("The bfd buffer is too small\n");
243+
return ERR_PTR(-EIO);
244+
}
245+
goto refill;
201246
}
202247
/*
203248
* Last bytes may lack the \n at the
@@ -216,6 +261,7 @@ char *breadchr(struct bfd *f, char c)
216261
return b->data;
217262
}
218263

264+
refill:
219265
/*
220266
* small optimization -- we've scanned b->sz
221267
* symbols already, no need to re-scan them after
@@ -252,14 +298,14 @@ static int __bwrite(struct bfd *bfd, const void *buf, int size)
252298
{
253299
struct xbuf *b = &bfd->b;
254300

255-
if (b->sz + size > BUFSIZE) {
301+
if (b->sz + size > b->bsize) {
256302
int ret;
257303
ret = bflush(bfd);
258304
if (ret < 0)
259305
return ret;
260306
}
261307

262-
if (size > BUFSIZE)
308+
if (size > b->bsize)
263309
return write_all(bfd->fd, buf, size);
264310

265311
memcpy(b->data + b->sz, buf, size);

criu/include/bfd.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ struct xbuf {
88
char *mem; /* buffer */
99
char *data; /* position we see bytes at */
1010
unsigned int sz; /* bytes sitting after b->pos */
11+
unsigned int bsize;
1112
struct bfd_buf *buf;
1213
};
1314

criu/unittest/unit.c

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,97 @@
11
#include <stdio.h>
22
#include <stdlib.h>
33
#include <assert.h>
4+
#include <unistd.h>
5+
#include <fcntl.h>
6+
#include <sys/mman.h>
47

58
#include "log.h"
69
#include "util.h"
710
#include "criu-log.h"
11+
#include "bfd.h"
812

913
int parse_statement(int i, char *line, char **configuration);
1014

15+
static void test_bfd(void)
16+
{
17+
struct bfd f;
18+
char *str;
19+
const int lines = 5;
20+
char *long_line[lines];
21+
int size = 1024 * 1024;
22+
int i, fd;
23+
24+
fd = memfd_create("criu-bfd-test", 0);
25+
assert(fd >= 0);
26+
27+
for (i = 0; i < lines; i++) {
28+
int j;
29+
30+
long_line[i] = malloc(size + 2);
31+
assert(long_line[i]);
32+
long_line[i][0] = 'A' + (i % 26);
33+
for (j = 1; j < size; j++)
34+
long_line[i][j] = 'a' + (j % 26);
35+
long_line[i][size] = '\n';
36+
long_line[i][size + 1] = '\0';
37+
38+
assert(write(fd, long_line[i], size + 1) == size + 1);
39+
}
40+
assert(lseek(fd, 0, SEEK_SET) == 0);
41+
42+
f.fd = fd;
43+
assert(bfdopenr(&f) == 0);
44+
45+
for (i = 0; i < lines; i++) {
46+
str = breadline(&f);
47+
assert(str);
48+
assert(strlen(str) == size);
49+
/* long_line has \n, str hasn't */
50+
assert(strcmp(str, long_line[i]) != 0);
51+
str[size] = '\n';
52+
assert(memcmp(str, long_line[i], size + 1) == 0);
53+
}
54+
55+
bclose(&f);
56+
for (i = 0; i < lines; i++)
57+
free(long_line[i]);
58+
}
59+
60+
static void test_bwrite(void)
61+
{
62+
struct bfd f;
63+
char *buf;
64+
int size = 1024 * 1024;
65+
int i;
66+
int fd;
67+
char *read_buf;
68+
69+
fd = memfd_create("criu-bfd-test", 0);
70+
assert(fd >= 0);
71+
72+
buf = malloc(size);
73+
assert(buf);
74+
for (i = 0; i < size; i++)
75+
buf[i] = 'z' - (i % 26);
76+
77+
f.fd = dup(fd);
78+
assert(f.fd >= 0);
79+
assert(bfdopenw(&f) == 0);
80+
81+
assert(bwrite(&f, buf, size) == size);
82+
bclose(&f);
83+
84+
assert(lseek(fd, 0, SEEK_SET) == 0);
85+
read_buf = malloc(size);
86+
assert(read_buf);
87+
assert(read(fd, read_buf, size) == size);
88+
assert(memcmp(buf, read_buf, size) == 0);
89+
90+
close(fd);
91+
free(buf);
92+
free(read_buf);
93+
}
94+
1195
int main(int argc, char *argv[], char *envp[])
1296
{
1397
char **configuration;
@@ -16,6 +100,9 @@ int main(int argc, char *argv[], char *envp[])
16100
configuration = malloc(10 * sizeof(char *));
17101
log_init(NULL);
18102

103+
test_bfd();
104+
test_bwrite();
105+
19106
i = parse_statement(0, "", configuration);
20107
assert(i == 0);
21108

0 commit comments

Comments
 (0)