Index: share/man/man9/sbuf.9 =================================================================== --- share/man/man9/sbuf.9 +++ share/man/man9/sbuf.9 @@ -25,7 +25,7 @@ .\" .\" $FreeBSD$ .\" -.Dd April 5, 2017 +.Dd August 8, 2017 .Dt SBUF 9 .Os .Sh NAME @@ -271,6 +271,14 @@ 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 Index: sys/kern/subr_sbuf.c =================================================================== --- sys/kern/subr_sbuf.c +++ sys/kern/subr_sbuf.c @@ -73,6 +73,8 @@ #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_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 @@ 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 @@ 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 @@ ("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 @@ } 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; Index: sys/sys/sbuf.h =================================================================== --- sys/sys/sbuf.h +++ sys/sys/sbuf.h @@ -49,6 +49,7 @@ #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 @@ #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