Implement simple record boundary tracking in sbuf(9) to avoid record splitting

during drain operations. When an sbuf is configured to use this feature by way
of the SBUF_DRAINTOEOR sbuf_new() flag, top-level sections started with
sbuf_start_section() create a record boundary marker that is used to avoid
flushing partial records.

Reviewed by:	cem,imp,wblock
MFC after:	2 weeks
Sponsored by:	Netflix, Inc.
Differential Revision:	https://reviews.freebsd.org/D8536
This commit is contained in:
lstewart 2017-08-17 07:20:09 +00:00
parent cefde59f8f
commit 0923281ac1
3 changed files with 21 additions and 3 deletions

View File

@ -25,7 +25,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd April 5, 2017
.Dd August 17, 2017
.Dt SBUF 9
.Os
.Sh NAME
@ -271,6 +271,14 @@ This indicates that the storage buffer may be extended as necessary, so long
as resources allow, to hold additional data.
.It Dv SBUF_INCLUDENUL
This causes the final nulterm byte to be counted in the length of the data.
.It Dv SBUF_DRAINTOEOR
Treat top-level sections started with
.Fn sbuf_start_section
as a record boundary marker that will be used during drain operations to avoid
records being split.
If a record grows sufficiently large such that it fills the
.Fa sbuf
and therefore cannot be drained without being split, an error of EDEADLK is set.
.El
.Pp
Note that if

View File

@ -73,6 +73,8 @@ static MALLOC_DEFINE(M_SBUF, "sbuf", "string buffers");
#define SBUF_CANEXTEND(s) ((s)->s_flags & SBUF_AUTOEXTEND)
#define SBUF_ISSECTION(s) ((s)->s_flags & SBUF_INSECTION)
#define SBUF_NULINCLUDED(s) ((s)->s_flags & SBUF_INCLUDENUL)
#define SBUF_ISDRAINTOEOR(s) ((s)->s_flags & SBUF_DRAINTOEOR)
#define SBUF_DODRAINTOEOR(s) (SBUF_ISSECTION(s) && SBUF_ISDRAINTOEOR(s))
/*
* Set / clear flags
@ -308,6 +310,7 @@ sbuf_clear(struct sbuf *s)
SBUF_CLEARFLAG(s, SBUF_FINISHED);
s->s_error = 0;
s->s_len = 0;
s->s_rec_off = 0;
s->s_sect_len = 0;
}
@ -362,7 +365,10 @@ sbuf_drain(struct sbuf *s)
KASSERT(s->s_len > 0, ("Shouldn't drain empty sbuf %p", s));
KASSERT(s->s_error == 0, ("Called %s with error on %p", __func__, s));
len = s->s_drain_func(s->s_drain_arg, s->s_buf, s->s_len);
if (SBUF_DODRAINTOEOR(s) && s->s_rec_off == 0)
return (s->s_error = EDEADLK);
len = s->s_drain_func(s->s_drain_arg, s->s_buf,
SBUF_DODRAINTOEOR(s) ? s->s_rec_off : s->s_len);
if (len < 0) {
s->s_error = -len;
return (s->s_error);
@ -370,6 +376,7 @@ sbuf_drain(struct sbuf *s)
KASSERT(len > 0 && len <= s->s_len,
("Bad drain amount %d for sbuf %p", len, s));
s->s_len -= len;
s->s_rec_off -= len;
/*
* Fast path for the expected case where all the data was
* drained.
@ -835,6 +842,7 @@ sbuf_start_section(struct sbuf *s, ssize_t *old_lenp)
("s_sect_len != 0 when starting a section"));
if (old_lenp != NULL)
*old_lenp = -1;
s->s_rec_off = s->s_len;
SBUF_SETFLAG(s, SBUF_INSECTION);
} else {
KASSERT(old_lenp != NULL,
@ -865,7 +873,7 @@ sbuf_end_section(struct sbuf *s, ssize_t old_len, size_t pad, int c)
}
len = s->s_sect_len;
if (old_len == -1) {
s->s_sect_len = 0;
s->s_rec_off = s->s_sect_len = 0;
SBUF_CLEARFLAG(s, SBUF_INSECTION);
} else {
s->s_sect_len += old_len;

View File

@ -49,6 +49,7 @@ struct sbuf {
#define SBUF_FIXEDLEN 0x00000000 /* fixed length buffer (default) */
#define SBUF_AUTOEXTEND 0x00000001 /* automatically extend buffer */
#define SBUF_INCLUDENUL 0x00000002 /* nulterm byte is counted in len */
#define SBUF_DRAINTOEOR 0x00000004 /* use section 0 as drain EOR marker */
#define SBUF_USRFLAGMSK 0x0000ffff /* mask of flags the user may specify */
#define SBUF_DYNAMIC 0x00010000 /* s_buf must be freed */
#define SBUF_FINISHED 0x00020000 /* set by sbuf_finish() */
@ -56,6 +57,7 @@ struct sbuf {
#define SBUF_INSECTION 0x00100000 /* set by sbuf_start_section() */
int s_flags; /* flags */
ssize_t s_sect_len; /* current length of section */
ssize_t s_rec_off; /* current record start offset */
};
#ifndef HD_COLUMN_MASK