svn commit: r212367 - in head: share/man/man9 sys/kern sys/sys

Matthew D Fleming mdf at FreeBSD.org
Thu Sep 9 17:49:19 UTC 2010


Author: mdf
Date: Thu Sep  9 17:49:18 2010
New Revision: 212367
URL: http://svn.freebsd.org/changeset/base/212367

Log:
  Add drain functionality to sbufs.  The drain is a function that is
  called when the sbuf internal buffer is filled.  For kernel sbufs with a
  drain, the internal buffer will never be expanded.  For userland sbufs
  with a drain, the internal buffer may still be expanded by
  sbuf_[v]printf(3).
  
  Sbufs now have three basic uses:
  1) static string manipulation.  Overflow is marked.
  2) dynamic string manipulation.  Overflow triggers string growth.
  3) drained string manipulation.  Overflow triggers draining.
  
  In all cases the manipulation is 'safe' in that overflow is detected and
  managed.
  
  Reviewed by:	phk (the previous version)

Modified:
  head/share/man/man9/Makefile
  head/share/man/man9/sbuf.9
  head/sys/kern/subr_sbuf.c
  head/sys/sys/sbuf.h

Modified: head/share/man/man9/Makefile
==============================================================================
--- head/share/man/man9/Makefile	Thu Sep  9 17:45:48 2010	(r212366)
+++ head/share/man/man9/Makefile	Thu Sep  9 17:49:18 2010	(r212367)
@@ -1031,6 +1031,7 @@ MLINKS+=sbuf.9 sbuf_bcat.9 \
 	sbuf.9 sbuf_overflowed.9 \
 	sbuf.9 sbuf_printf.9 \
 	sbuf.9 sbuf_putc.9 \
+	sbuf.9 sbuf_set_drain.9 \
 	sbuf.9 sbuf_setpos.9 \
 	sbuf.9 sbuf_trim.9 \
 	sbuf.9 sbuf_vprintf.9

Modified: head/share/man/man9/sbuf.9
==============================================================================
--- head/share/man/man9/sbuf.9	Thu Sep  9 17:45:48 2010	(r212366)
+++ head/share/man/man9/sbuf.9	Thu Sep  9 17:49:18 2010	(r212367)
@@ -43,6 +43,7 @@
 .Nm sbuf_printf ,
 .Nm sbuf_vprintf ,
 .Nm sbuf_putc ,
+.Nm sbuf_set_drain ,
 .Nm sbuf_trim ,
 .Nm sbuf_overflowed ,
 .Nm sbuf_finish ,
@@ -54,6 +55,8 @@
 .Sh SYNOPSIS
 .In sys/types.h
 .In sys/sbuf.h
+.Ft typedef\ int ( sbuf_drain_func ) ( void\ *arg, const\ char\ *data, int\ len ) ;
+.Pp
 .Ft struct sbuf *
 .Fn sbuf_new "struct sbuf *s" "char *buf" "int length" "int flags"
 .Ft struct sbuf *
@@ -80,11 +83,13 @@
 .Fn sbuf_vprintf "struct sbuf *s" "const char *fmt" "va_list ap"
 .Ft int
 .Fn sbuf_putc "struct sbuf *s" "int c"
+.Ft void
+.Fn sbuf_set_drain "struct sbuf *s" "sbuf_drain_func *func" "void *arg"
 .Ft int
 .Fn sbuf_trim "struct sbuf *s"
 .Ft int
 .Fn sbuf_overflowed "struct sbuf *s"
-.Ft void
+.Ft int
 .Fn sbuf_finish "struct sbuf *s"
 .Ft char *
 .Fn sbuf_data "struct sbuf *s"
@@ -224,6 +229,51 @@ to the
 at the current position.
 .Pp
 The
+.Fn sbuf_set_drain
+function sets a drain function
+.Fa func
+for the
+.Fa sbuf ,
+and records a pointer
+.Fa arg
+to be passed to the drain on callback.
+The drain function cannot be changed while
+.Fa sbuf_len
+is non-zero.
+.Pp
+The registered drain function
+.Vt sbuf_drain_func
+will be called with the argument
+.Fa arg
+provided to
+.Fn sbuf_set_drain ,
+a pointer
+.Fa data
+to a byte string that is the contents of the sbuf, and the length
+.Fa len
+of the data.
+If the drain function exists, it will be called when the sbuf internal
+buffer is full, or on behalf of
+.Fn sbuf_finish .
+The drain function may drain some or all of the data, but must drain
+at least 1 byte.
+The return value from the drain function, if positive, indicates how
+many bytes were drained.
+If negative, the return value indicates the negative error code which
+will be returned from this or a later call to
+.Fn sbuf_finish .
+The returned drained length cannot be zero.
+To do unbuffered draining, initialize the sbuf with a two-byte buffer.
+The drain will be called for every byte added to the sbuf.
+The
+.Fn sbuf_bcopyin ,
+.Fn sbuf_copyin ,
+.Fn sbuf_trim ,
+and
+.Fn sbuf_data
+functions cannot be used on an sbuf with a drain.
+.Pp
+The
 .Fn sbuf_copyin
 function copies a NUL-terminated string from the specified userland
 address into the
@@ -289,10 +339,17 @@ overflowed.
 .Pp
 The
 .Fn sbuf_finish
-function NUL-terminates the
+function will call the attached drain function if one exists until all
+the data in the
 .Fa sbuf
-and marks it as finished, which means that it may no longer be
-modified using
+is flushed.
+If there is no attached drain,
+.Fn sbuf_finish
+NUL-terminates the
+.Fa sbuf .
+In either case it marks the
+.Fa sbuf
+as finished, which means that it may no longer be modified using
 .Fn sbuf_setpos ,
 .Fn sbuf_cat ,
 .Fn sbuf_cpy ,
@@ -305,12 +362,21 @@ is used to reset the sbuf.
 .Pp
 The
 .Fn sbuf_data
-and
-.Fn sbuf_len
-functions return the actual string and its length, respectively;
+function returns the actual string;
 .Fn sbuf_data
 only works on a finished
 .Fa sbuf .
+The
+.Fn sbuf_len function returns the length of the string.
+For an
+.Fa sbuf
+with an attached drain,
+.Fn sbuf_len
+returns the length of the un-drained data.
+.Fn sbuf_done
+returns non-zero if the
+.Fa sbuf
+is finished.
 .Fn sbuf_done
 returns non-zero if the
 .Fa sbuf
@@ -329,6 +395,22 @@ size of its storage buffer using
 .Fn sbuf_setpos ,
 or it is reinitialized to a sufficiently short string using
 .Fn sbuf_cpy .
+.Pp
+Drains in user-space will not always function as indicated.
+While the drain function will be called immediately on overflow from
+the
+.Fa sbuf_putc ,
+.Fa sbuf_bcat ,
+.Fa sbuf_cat
+functions,
+.Fa sbuf_printf
+and
+.Fa sbuf_vprintf
+currently have no way to determine whether there will be an overflow
+until after it occurs, and cannot do a partial expansion of the format
+string.
+Thus when using libsbuf the buffer may be extended to allow completion
+of a single printf call, even though a drain is attached.
 .Sh RETURN VALUES
 The
 .Fn sbuf_new
@@ -372,6 +454,14 @@ The
 function
 returns \-1 if copying string from userland failed, and number of bytes
 copied otherwise.
+The
+.Fn sbuf_finish
+function returns ENOMEM if the sbuf overflowed before being finished,
+or returns the error code from the drain if one is attached.
+When used as
+.Xr sbuf_finish 3 ,
+.Fn sbuf_finish
+will return \-1 and set errno on error instead.
 .Sh SEE ALSO
 .Xr printf 3 ,
 .Xr strcat 3 ,
@@ -396,6 +486,8 @@ Additional improvements were suggested b
 .An Justin T. Gibbs Aq gibbs at FreeBSD.org .
 Auto-extend support added by
 .An Kelly Yancey Aq kbyanc at FreeBSD.org .
+Drain functionality added by
+.An Matthew Fleming Aq mdf at FreeBSD.org .
 .Pp
 This manual page was written by
 .An Dag-Erling Sm\(/orgrav Aq des at FreeBSD.org .

Modified: head/sys/kern/subr_sbuf.c
==============================================================================
--- head/sys/kern/subr_sbuf.c	Thu Sep  9 17:45:48 2010	(r212366)
+++ head/sys/kern/subr_sbuf.c	Thu Sep  9 17:49:18 2010	(r212367)
@@ -33,6 +33,7 @@ __FBSDID("$FreeBSD$");
 
 #ifdef _KERNEL
 #include <sys/ctype.h>
+#include <sys/errno.h>
 #include <sys/kernel.h>
 #include <sys/malloc.h>
 #include <sys/systm.h>
@@ -40,6 +41,7 @@ __FBSDID("$FreeBSD$");
 #include <machine/stdarg.h>
 #else /* _KERNEL */
 #include <ctype.h>
+#include <errno.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -246,6 +248,7 @@ sbuf_clear(struct sbuf *s)
 
 	SBUF_CLEARFLAG(s, SBUF_FINISHED);
 	SBUF_CLEARFLAG(s, SBUF_OVERFLOWED);
+	s->s_error = 0;
 	s->s_len = 0;
 }
 
@@ -272,6 +275,54 @@ sbuf_setpos(struct sbuf *s, int pos)
 }
 
 /*
+ * Set up a drain function and argument on an sbuf to flush data to
+ * when the sbuf buffer overflows.
+ */
+void
+sbuf_set_drain(struct sbuf *s, sbuf_drain_func *func, void *ctx)
+{
+
+	assert_sbuf_state(s, 0);
+	assert_sbuf_integrity(s);
+	KASSERT(func == s->s_drain_func || s->s_len == 0,
+	    ("Cannot change drain to %p on non-empty sbuf %p", func, s));
+	s->s_drain_func = func;
+	s->s_drain_arg = ctx;
+}
+
+/*
+ * Call the drain and process the return.
+ */
+static int
+sbuf_drain(struct sbuf *s)
+{
+	int len;
+
+	KASSERT(s->s_len > 0, ("Shouldn't drain empty sbuf %p", s));
+	len = s->s_drain_func(s->s_drain_arg, s->s_buf, s->s_len);
+	if (len < 0) {
+		s->s_error = -len;
+		SBUF_SETFLAG(s, SBUF_OVERFLOWED);
+		return (s->s_error);
+	}
+
+	KASSERT(len > 0, ("Drain must either error or work!"));
+	s->s_len -= len;
+	/*
+	 * Fast path for the expected case where all the data was
+	 * drained.
+	 */
+	if (s->s_len == 0)
+		return (0);
+	/*
+	 * Move the remaining characters to the beginning of the
+	 * string.
+	 */
+	memmove(s->s_buf, s->s_buf + len, s->s_len);
+	return (0);
+}
+
+/*
  * Append a byte to an sbuf.  This is the core function for appending
  * to an sbuf and is the main place that deals with extending the
  * buffer and marking overflow.
@@ -286,10 +337,16 @@ sbuf_put_byte(int c, struct sbuf *s)
 	if (SBUF_HASOVERFLOWED(s))
 		return;
 	if (SBUF_FREESPACE(s) <= 0) {
-		if (sbuf_extend(s, 1) < 0) {
+		/* 
+		 * If there is a drain, use it, otherwise extend the
+		 * buffer.
+		 */
+		if (s->s_drain_func != NULL)
+			(void)sbuf_drain(s);
+		else if (sbuf_extend(s, 1) < 0)
 			SBUF_SETFLAG(s, SBUF_OVERFLOWED);
+		if (SBUF_HASOVERFLOWED(s))
 			return;
-		}
 	}
 	s->s_buf[s->s_len++] = c;
 }
@@ -338,6 +395,8 @@ sbuf_bcopyin(struct sbuf *s, const void 
 
 	assert_sbuf_integrity(s);
 	assert_sbuf_state(s, 0);
+	KASSERT(s->s_drain_func == NULL,
+	    ("Nonsensical copyin to sbuf %p with a drain", s));
 
 	if (SBUF_HASOVERFLOWED(s))
 		return (-1);
@@ -402,6 +461,8 @@ sbuf_copyin(struct sbuf *s, const void *
 
 	assert_sbuf_integrity(s);
 	assert_sbuf_state(s, 0);
+	KASSERT(s->s_drain_func == NULL,
+	    ("Nonsensical copyin to sbuf %p with a drain", s));
 
 	if (SBUF_HASOVERFLOWED(s))
 		return (-1);
@@ -466,7 +527,7 @@ int
 sbuf_vprintf(struct sbuf *s, const char *fmt, va_list ap)
 {
 	va_list ap_copy;
-	int len;
+	int error, len;
 
 	assert_sbuf_integrity(s);
 	assert_sbuf_state(s, 0);
@@ -481,15 +542,28 @@ sbuf_vprintf(struct sbuf *s, const char 
 	 * For the moment, there is no way to get vsnprintf(3) to hand
 	 * back a character at a time, to push everything into
 	 * sbuf_putc_func() as was done for the kernel.
+	 *
+	 * In userspace, while drains are useful, there's generally
+	 * not a problem attempting to malloc(3) on out of space.  So
+	 * expand a userland sbuf if there is not enough room for the
+	 * data produced by sbuf_[v]printf(3).
 	 */
 
+	error = 0;
 	do {
 		va_copy(ap_copy, ap);
 		len = vsnprintf(&s->s_buf[s->s_len], SBUF_FREESPACE(s) + 1,
 		    fmt, ap_copy);
 		va_end(ap_copy);
-	} while (len > SBUF_FREESPACE(s) &&
-	    sbuf_extend(s, len - SBUF_FREESPACE(s)) == 0);
+
+		if (SBUF_FREESPACE(s) >= len)
+			break;
+		/* Cannot print with the current available space. */
+		if (s->s_drain_func != NULL && s->s_len > 0)
+			error = sbuf_drain(s);
+		else
+			error = sbuf_extend(s, len - SBUF_FREESPACE(s));
+	} while (error == 0);
 
 	/*
 	 * s->s_len is the length of the string, without the terminating nul.
@@ -552,6 +626,8 @@ sbuf_trim(struct sbuf *s)
 
 	assert_sbuf_integrity(s);
 	assert_sbuf_state(s, 0);
+	KASSERT(s->s_drain_func == NULL,
+	    ("%s makes no sense on sbuf %p with drain", __func__, s));
 
 	if (SBUF_HASOVERFLOWED(s))
 		return (-1);
@@ -575,16 +651,29 @@ sbuf_overflowed(struct sbuf *s)
 /*
  * Finish off an sbuf.
  */
-void
+int
 sbuf_finish(struct sbuf *s)
 {
+	int error = 0;
 
 	assert_sbuf_integrity(s);
 	assert_sbuf_state(s, 0);
 
+	if (s->s_drain_func != NULL) {
+		error = s->s_error;
+		while (s->s_len > 0 && error == 0)
+			error = sbuf_drain(s);
+	} else if (SBUF_HASOVERFLOWED(s))
+		error = ENOMEM;
 	s->s_buf[s->s_len] = '\0';
 	SBUF_CLEARFLAG(s, SBUF_OVERFLOWED);
 	SBUF_SETFLAG(s, SBUF_FINISHED);
+#ifdef _KERNEL
+	return (error);
+#else
+	errno = error;
+	return (-1);
+#endif
 }
 
 /*
@@ -596,6 +685,8 @@ sbuf_data(struct sbuf *s)
 
 	assert_sbuf_integrity(s);
 	assert_sbuf_state(s, SBUF_FINISHED);
+	KASSERT(s->s_drain_func == NULL,
+	    ("%s makes no sense on sbuf %p with drain", __func__, s));
 
 	return (s->s_buf);
 }
@@ -609,6 +700,8 @@ sbuf_len(struct sbuf *s)
 
 	assert_sbuf_integrity(s);
 	/* don't care if it's finished or not */
+	KASSERT(s->s_drain_func == NULL,
+	    ("%s makes no sense on sbuf %p with drain", __func__, s));
 
 	if (SBUF_HASOVERFLOWED(s))
 		return (-1);

Modified: head/sys/sys/sbuf.h
==============================================================================
--- head/sys/sys/sbuf.h	Thu Sep  9 17:45:48 2010	(r212366)
+++ head/sys/sys/sbuf.h	Thu Sep  9 17:49:18 2010	(r212367)
@@ -33,12 +33,17 @@
 
 #include <sys/_types.h>
 
+struct sbuf;
+typedef int (sbuf_drain_func)(void *, const char *, int);
+
 /*
  * Structure definition
  */
 struct sbuf {
 	char		*s_buf;		/* storage buffer */
-	void		*s_unused;	/* binary compatibility. */
+	sbuf_drain_func	*s_drain_func;	/* drain function */
+	void		*s_drain_arg;	/* user-supplied drain argument */
+	int		 s_error;	/* current error code */
 	int		 s_size;	/* size of storage buffer */
 	int		 s_len;		/* current length of string */
 #define	SBUF_FIXEDLEN	0x00000000	/* fixed length buffer (default) */
@@ -69,9 +74,10 @@ int		 sbuf_printf(struct sbuf *, const c
 int		 sbuf_vprintf(struct sbuf *, const char *, __va_list)
 	__printflike(2, 0);
 int		 sbuf_putc(struct sbuf *, int);
+void		 sbuf_set_drain(struct sbuf *, sbuf_drain_func *, void *);
 int		 sbuf_trim(struct sbuf *);
 int		 sbuf_overflowed(struct sbuf *);
-void		 sbuf_finish(struct sbuf *);
+int		 sbuf_finish(struct sbuf *);
 char		*sbuf_data(struct sbuf *);
 int		 sbuf_len(struct sbuf *);
 int		 sbuf_done(struct sbuf *);


More information about the svn-src-all mailing list