svn commit: r347229 - in head: lib/libsbuf lib/libsbuf/tests share/man/man9 sys/kern sys/sys
Conrad Meyer
cem at FreeBSD.org
Tue May 7 17:47:23 UTC 2019
Author: cem
Date: Tue May 7 17:47:20 2019
New Revision: 347229
URL: https://svnweb.freebsd.org/changeset/base/347229
Log:
device_printf: Use sbuf for more coherent prints on SMP
device_printf does multiple calls to printf allowing other console messages to
be inserted between the device name, and the rest of the message. This change
uses sbuf to compose to two into a single buffer, and prints it all at once.
It exposes an sbuf drain function (drain-to-printf) for common use.
Update documentation to match; some unit tests included.
Submitted by: jmg
Sponsored by: Dell EMC Isilon
Differential Revision: https://reviews.freebsd.org/D16690
Modified:
head/lib/libsbuf/Symbol.map
head/lib/libsbuf/tests/sbuf_core_test.c
head/lib/libsbuf/tests/sbuf_stdio_test.c
head/share/man/man9/Makefile
head/share/man/man9/sbuf.9
head/sys/kern/kern_sysctl.c
head/sys/kern/subr_bus.c
head/sys/kern/subr_prf.c
head/sys/sys/sbuf.h
Modified: head/lib/libsbuf/Symbol.map
==============================================================================
--- head/lib/libsbuf/Symbol.map Tue May 7 16:17:33 2019 (r347228)
+++ head/lib/libsbuf/Symbol.map Tue May 7 17:47:20 2019 (r347229)
@@ -37,5 +37,6 @@ FBSD_1.4 {
FBSD_1.5 {
sbuf_putbuf;
+ sbuf_printf_drain;
};
Modified: head/lib/libsbuf/tests/sbuf_core_test.c
==============================================================================
--- head/lib/libsbuf/tests/sbuf_core_test.c Tue May 7 16:17:33 2019 (r347228)
+++ head/lib/libsbuf/tests/sbuf_core_test.c Tue May 7 17:47:20 2019 (r347229)
@@ -63,6 +63,9 @@ ATF_TC_BODY(sbuf_clear_test, tc)
*/
child_proc = atf_utils_fork();
if (child_proc == 0) {
+ ATF_REQUIRE_EQ_MSG(0, sbuf_finish(sb), "sbuf_finish failed: %s",
+ strerror(errno));
+
sbuf_putbuf(sb);
exit(0);
}
@@ -100,6 +103,34 @@ ATF_TC_BODY(sbuf_done_and_sbuf_finish_test, tc)
sbuf_delete(sb);
}
+static int
+drain_ret0(void *arg, const char *data, int len)
+{
+
+ (void)arg;
+ (void)data;
+ (void)len;
+
+ return (0);
+}
+
+ATF_TC_WITHOUT_HEAD(sbuf_drain_ret0_test);
+ATF_TC_BODY(sbuf_drain_ret0_test, tc)
+{
+ struct sbuf *sb;
+
+ sb = sbuf_new_auto();
+
+ sbuf_set_drain(sb, drain_ret0, NULL);
+
+ sbuf_cat(sb, test_string);
+
+ ATF_CHECK_EQ_MSG(-1, sbuf_finish(sb),
+ "required to return error when drain func returns 0");
+ ATF_CHECK_EQ_MSG(EDEADLK, errno,
+ "errno required to be EDEADLK when drain func returns 0");
+}
+
ATF_TC_WITHOUT_HEAD(sbuf_len_test);
ATF_TC_BODY(sbuf_len_test, tc)
{
@@ -131,6 +162,34 @@ ATF_TC_BODY(sbuf_len_test, tc)
sbuf_delete(sb);
}
+ATF_TC_WITHOUT_HEAD(sbuf_new_fixedlen);
+ATF_TC_BODY(sbuf_new_fixedlen, tc)
+{
+ char buf[strlen(test_string) + 1];
+ struct sbuf sb;
+ pid_t child_proc;
+
+ sbuf_new(&sb, buf, sizeof(buf), SBUF_FIXEDLEN);
+
+ sbuf_cat(&sb, test_string);
+
+ child_proc = atf_utils_fork();
+ if (child_proc == 0) {
+ ATF_REQUIRE_EQ_MSG(0, sbuf_finish(&sb), "sbuf_finish failed: %s",
+ strerror(errno));
+
+ sbuf_putbuf(&sb);
+ exit(0);
+ }
+ atf_utils_wait(child_proc, 0, test_string, "");
+
+ sbuf_putc(&sb, ' ');
+
+ ATF_CHECK_EQ_MSG(-1, sbuf_finish(&sb), "failed to return error on overflow");
+
+ sbuf_delete(&sb);
+}
+
ATF_TC_WITHOUT_HEAD(sbuf_setpos_test);
ATF_TC_BODY(sbuf_setpos_test, tc)
{
@@ -190,7 +249,9 @@ ATF_TP_ADD_TCS(tp)
ATF_TP_ADD_TC(tp, sbuf_clear_test);
ATF_TP_ADD_TC(tp, sbuf_done_and_sbuf_finish_test);
+ ATF_TP_ADD_TC(tp, sbuf_drain_ret0_test);
ATF_TP_ADD_TC(tp, sbuf_len_test);
+ ATF_TP_ADD_TC(tp, sbuf_new_fixedlen);
#if 0
/* TODO */
#ifdef HAVE_SBUF_CLEAR_FLAGS
Modified: head/lib/libsbuf/tests/sbuf_stdio_test.c
==============================================================================
--- head/lib/libsbuf/tests/sbuf_stdio_test.c Tue May 7 16:17:33 2019 (r347228)
+++ head/lib/libsbuf/tests/sbuf_stdio_test.c Tue May 7 17:47:20 2019 (r347229)
@@ -59,6 +59,60 @@ sbuf_vprintf_helper(struct sbuf *sb, const char * rest
return (rc);
}
+ATF_TC_WITHOUT_HEAD(sbuf_printf_drain_null_test);
+ATF_TC_BODY(sbuf_printf_drain_null_test, tc)
+{
+ struct sbuf *sb;
+ char buf[2];
+ pid_t child_proc;
+
+ sb = sbuf_new(NULL, buf, sizeof(buf), SBUF_FIXEDLEN);
+ ATF_REQUIRE_MSG(sb != NULL, "sbuf_new_auto failed: %s",
+ strerror(errno));
+
+ child_proc = atf_utils_fork();
+ if (child_proc == 0) {
+ sbuf_set_drain(sb, sbuf_printf_drain, NULL);
+
+ ATF_REQUIRE_EQ_MSG(0, sbuf_cat(sb, test_string),
+ "sbuf_cat failed");
+
+ ATF_CHECK_EQ(0, sbuf_finish(sb));
+ exit(0);
+ }
+ atf_utils_wait(child_proc, 0, test_string, "");
+
+ sbuf_delete(sb);
+}
+
+ATF_TC_WITHOUT_HEAD(sbuf_printf_drain_test);
+ATF_TC_BODY(sbuf_printf_drain_test, tc)
+{
+ struct sbuf *sb;
+ char buf[2];
+ pid_t child_proc;
+ size_t cnt = 0;
+
+ sb = sbuf_new(NULL, buf, sizeof(buf), SBUF_FIXEDLEN);
+ ATF_REQUIRE_MSG(sb != NULL, "sbuf_new_auto failed: %s",
+ strerror(errno));
+
+ child_proc = atf_utils_fork();
+ if (child_proc == 0) {
+ sbuf_set_drain(sb, sbuf_printf_drain, &cnt);
+
+ ATF_REQUIRE_EQ_MSG(0, sbuf_cat(sb, test_string),
+ "sbuf_cat failed");
+
+ ATF_CHECK_EQ(0, sbuf_finish(sb));
+ ATF_CHECK_EQ(strlen(test_string), cnt);
+ exit(0);
+ }
+ atf_utils_wait(child_proc, 0, test_string, "");
+
+ sbuf_delete(sb);
+}
+
ATF_TC_WITHOUT_HEAD(sbuf_printf_test);
ATF_TC_BODY(sbuf_printf_test, tc)
{
@@ -106,6 +160,7 @@ ATF_TC_BODY(sbuf_putbuf_test, tc)
child_proc = atf_utils_fork();
if (child_proc == 0) {
+ ATF_CHECK_EQ(0, sbuf_finish(sb));
sbuf_putbuf(sb);
exit(0);
}
@@ -152,6 +207,8 @@ ATF_TC_BODY(sbuf_vprintf_test, tc)
ATF_TP_ADD_TCS(tp)
{
+ ATF_TP_ADD_TC(tp, sbuf_printf_drain_null_test);
+ ATF_TP_ADD_TC(tp, sbuf_printf_drain_test);
ATF_TP_ADD_TC(tp, sbuf_printf_test);
ATF_TP_ADD_TC(tp, sbuf_putbuf_test);
ATF_TP_ADD_TC(tp, sbuf_vprintf_test);
Modified: head/share/man/man9/Makefile
==============================================================================
--- head/share/man/man9/Makefile Tue May 7 16:17:33 2019 (r347228)
+++ head/share/man/man9/Makefile Tue May 7 17:47:20 2019 (r347229)
@@ -1780,6 +1780,8 @@ MLINKS+=sbuf.9 sbuf_bcat.9 \
sbuf.9 sbuf_new_auto.9 \
sbuf.9 sbuf_new_for_sysctl.9 \
sbuf.9 sbuf_printf.9 \
+ sbuf.9 sbuf_printf_drain.9 \
+ sbuf.9 sbuf_putbuf.9 \
sbuf.9 sbuf_putc.9 \
sbuf.9 sbuf_set_drain.9 \
sbuf.9 sbuf_set_flags.9 \
Modified: head/share/man/man9/sbuf.9
==============================================================================
--- head/share/man/man9/sbuf.9 Tue May 7 16:17:33 2019 (r347228)
+++ head/share/man/man9/sbuf.9 Tue May 7 17:47:20 2019 (r347229)
@@ -25,7 +25,7 @@
.\"
.\" $FreeBSD$
.\"
-.Dd May 23, 2018
+.Dd May 7, 2019
.Dt SBUF 9
.Os
.Sh NAME
@@ -58,6 +58,7 @@
.Nm sbuf_start_section ,
.Nm sbuf_end_section ,
.Nm sbuf_hexdump ,
+.Nm sbuf_printf_drain ,
.Nm sbuf_putbuf
.Nd safe string composition
.Sh SYNOPSIS
@@ -191,6 +192,12 @@
.Fa "const char *hdr"
.Fa "int flags"
.Fc
+.Ft int
+.Fo sbuf_printf_drain
+.Fa "void *arg"
+.Fa "const char *data"
+.Fa "int len"
+.Fc
.Ft void
.Fo sbuf_putbuf
.Fa "struct sbuf *s"
@@ -278,7 +285,9 @@ as a record boundary marker that will be used during d
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.
+and therefore cannot be drained without being split, an error of
+.Er EDEADLK
+is set.
.El
.Pp
Note that if
@@ -419,7 +428,9 @@ 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.
+If the returned drained length is 0, an error of
+.Er EDEADLK
+is set.
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
@@ -492,7 +503,9 @@ The
.Fn sbuf_error
function returns any error value that the
.Fa sbuf
-may have accumulated, either from the drain function, or ENOMEM if the
+may have accumulated, either from the drain function, or
+.Er ENOMEM
+if the
.Fa sbuf
overflowed.
This function is generally not needed and instead the error code from
@@ -574,9 +587,29 @@ See the
man page for more details on the interface.
.Pp
The
+.Fn sbuf_printf_drain
+function is a drain function that will call printf, or log to the console.
+The argument
+.Fa arg
+must be either
+.Dv NULL ,
+or a valid pointer to a
+.Vt size_t .
+If
+.Fa arg
+is not
+.Dv NULL ,
+the total bytes drained will be added to the value pointed to by
+.Fa arg .
+.Pp
+The
.Fn sbuf_putbuf
function printfs the sbuf to stdout if in userland, and to the console
and log if in the kernel.
+The
+.Fa sbuf
+must be finished before calling
+.Fn sbuf_putbuf .
It does not drain the buffer or update any pointers.
.Sh NOTES
If an operation caused an
@@ -655,8 +688,9 @@ function returns the section length or \-1 if the buff
.Pp
The
.Fn sbuf_finish 9
-function (the kernel version) returns ENOMEM if the sbuf overflowed before
-being finished,
+function (the kernel version) returns
+.Er ENOMEM
+if the sbuf overflowed before being finished,
or returns the error code from the drain if one is attached.
.Pp
The
Modified: head/sys/kern/kern_sysctl.c
==============================================================================
--- head/sys/kern/kern_sysctl.c Tue May 7 16:17:33 2019 (r347228)
+++ head/sys/kern/kern_sysctl.c Tue May 7 17:47:20 2019 (r347229)
@@ -322,13 +322,6 @@ sysctl_load_tunable_by_oid_locked(struct sysctl_oid *o
freeenv(penv);
}
-static int
-sbuf_printf_drain(void *arg __unused, const char *data, int len)
-{
-
- return (printf("%.*s", len, data));
-}
-
/*
* Locate the path to a given oid. Returns the length of the resulting path,
* or -1 if the oid was not found. nodes must have room for CTL_MAXNAME
Modified: head/sys/kern/subr_bus.c
==============================================================================
--- head/sys/kern/subr_bus.c Tue May 7 16:17:33 2019 (r347228)
+++ head/sys/kern/subr_bus.c Tue May 7 17:47:20 2019 (r347229)
@@ -2431,13 +2431,31 @@ device_print_prettyname(device_t dev)
int
device_printf(device_t dev, const char * fmt, ...)
{
+ char buf[128];
+ struct sbuf sb;
+ const char *name;
va_list ap;
- int retval;
+ size_t retval;
- retval = device_print_prettyname(dev);
+ retval = 0;
+
+ sbuf_new(&sb, buf, sizeof(buf), SBUF_FIXEDLEN);
+ sbuf_set_drain(&sb, sbuf_printf_drain, &retval);
+
+ name = device_get_name(dev);
+
+ if (name == NULL)
+ sbuf_cat(&sb, "unknown: ");
+ else
+ sbuf_printf(&sb, "%s%d: ", name, device_get_unit(dev));
+
va_start(ap, fmt);
- retval += vprintf(fmt, ap);
+ sbuf_vprintf(&sb, fmt, ap);
va_end(ap);
+
+ sbuf_finish(&sb);
+ sbuf_delete(&sb);
+
return (retval);
}
Modified: head/sys/kern/subr_prf.c
==============================================================================
--- head/sys/kern/subr_prf.c Tue May 7 16:17:33 2019 (r347228)
+++ head/sys/kern/subr_prf.c Tue May 7 17:47:20 2019 (r347229)
@@ -62,6 +62,8 @@ __FBSDID("$FreeBSD$");
#include <sys/syslog.h>
#include <sys/cons.h>
#include <sys/uio.h>
+#else /* !_KERNEL */
+#include <errno.h>
#endif
#include <sys/ctype.h>
#include <sys/sbuf.h>
@@ -1254,3 +1256,42 @@ sbuf_putbuf(struct sbuf *sb)
printf("%s", sbuf_data(sb));
}
#endif
+
+int
+sbuf_printf_drain(void *arg, const char *data, int len)
+{
+ size_t *retvalptr;
+ int r;
+#ifdef _KERNEL
+ char *dataptr;
+ char oldchr;
+
+ /*
+ * This is allowed as an extra byte is always resvered for
+ * terminating NUL byte. Save and restore the byte because
+ * we might be flushing a record, and there may be valid
+ * data after the buffer.
+ */
+ oldchr = data[len];
+ dataptr = __DECONST(char *, data);
+ dataptr[len] = '\0';
+
+ prf_putbuf(dataptr, TOLOG | TOCONS, -1);
+ r = len;
+
+ dataptr[len] = oldchr;
+
+#else /* !_KERNEL */
+
+ r = printf("%.*s", len, data);
+ if (r < 0)
+ return (-errno);
+
+#endif
+
+ retvalptr = arg;
+ if (retvalptr != NULL)
+ *retvalptr += r;
+
+ return (r);
+}
Modified: head/sys/sys/sbuf.h
==============================================================================
--- head/sys/sys/sbuf.h Tue May 7 16:17:33 2019 (r347228)
+++ head/sys/sys/sbuf.h Tue May 7 17:47:20 2019 (r347229)
@@ -103,6 +103,7 @@ void sbuf_start_section(struct sbuf *, ssize_t *);
ssize_t sbuf_end_section(struct sbuf *, ssize_t, size_t, int);
void sbuf_hexdump(struct sbuf *, const void *, int, const char *,
int);
+int sbuf_printf_drain(void *arg, const char *data, int len);
void sbuf_putbuf(struct sbuf *);
#ifdef _KERNEL
More information about the svn-src-head
mailing list