summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sbin/dmesg/dmesg.c4
-rw-r--r--sys/conf/files1
-rw-r--r--sys/kern/subr_log.c25
-rw-r--r--sys/kern/subr_msgbuf.c239
-rw-r--r--sys/kern/subr_prf.c120
-rw-r--r--sys/sys/msgbuf.h26
6 files changed, 296 insertions, 119 deletions
diff --git a/sbin/dmesg/dmesg.c b/sbin/dmesg/dmesg.c
index ad24e16..063e4b8 100644
--- a/sbin/dmesg/dmesg.c
+++ b/sbin/dmesg/dmesg.c
@@ -137,9 +137,7 @@ main(int argc, char *argv[])
errx(1, "kvm_read: %s", kvm_geterr(kd));
kvm_close(kd);
buflen = cur.msg_size;
- bufpos = cur.msg_bufx;
- if (bufpos >= buflen)
- bufpos = 0;
+ bufpos = MSGBUF_SEQ_TO_POS(&cur, cur.msg_wseq);
}
/*
diff --git a/sys/conf/files b/sys/conf/files
index 50bbf28..94adcbb 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -1091,6 +1091,7 @@ kern/subr_log.c standard
kern/subr_mbuf.c standard
kern/subr_mchain.c optional libmchain
kern/subr_module.c standard
+kern/subr_msgbuf.c standard
kern/subr_param.c standard
kern/subr_pcpu.c standard
kern/subr_power.c standard
diff --git a/sys/kern/subr_log.c b/sys/kern/subr_log.c
index d721e2e..031d081 100644
--- a/sys/kern/subr_log.c
+++ b/sys/kern/subr_log.c
@@ -126,11 +126,12 @@ logclose(dev_t dev, int flag, int mode, struct thread *td)
static int
logread(dev_t dev, struct uio *uio, int flag)
{
+ char buf[128];
struct msgbuf *mbp = msgbufp;
int error = 0, l, s;
s = splhigh();
- while (mbp->msg_bufr == mbp->msg_bufx) {
+ while (msgbuf_getcount(mbp) == 0) {
if (flag & IO_NDELAY) {
splx(s);
return (EWOULDBLOCK);
@@ -145,19 +146,13 @@ logread(dev_t dev, struct uio *uio, int flag)
logsoftc.sc_state &= ~LOG_RDWAIT;
while (uio->uio_resid > 0) {
- l = mbp->msg_bufx - mbp->msg_bufr;
- if (l < 0)
- l = mbp->msg_size - mbp->msg_bufr;
- l = imin(l, uio->uio_resid);
+ l = imin(sizeof(buf), uio->uio_resid);
+ l = msgbuf_getbytes(mbp, buf, l);
if (l == 0)
break;
- error = uiomove((char *)msgbufp->msg_ptr + mbp->msg_bufr,
- l, uio);
+ error = uiomove(buf, l, uio);
if (error)
break;
- mbp->msg_bufr += l;
- if (mbp->msg_bufr >= mbp->msg_size)
- mbp->msg_bufr = 0;
}
return (error);
}
@@ -172,7 +167,7 @@ logpoll(dev_t dev, int events, struct thread *td)
s = splhigh();
if (events & (POLLIN | POLLRDNORM)) {
- if (msgbufp->msg_bufr != msgbufp->msg_bufx)
+ if (msgbuf_getcount(msgbufp) > 0)
revents |= events & (POLLIN | POLLRDNORM);
else
selrecord(td, &logsoftc.sc_selp);
@@ -212,18 +207,12 @@ logtimeout(void *arg)
static int
logioctl(dev_t dev, u_long com, caddr_t data, int flag, struct thread *td)
{
- int l, s;
switch (com) {
/* return number of characters immediately available */
case FIONREAD:
- s = splhigh();
- l = msgbufp->msg_bufx - msgbufp->msg_bufr;
- splx(s);
- if (l < 0)
- l += msgbufp->msg_size;
- *(int *)data = l;
+ *(int *)data = msgbuf_getcount(msgbufp);
break;
case FIONBIO:
diff --git a/sys/kern/subr_msgbuf.c b/sys/kern/subr_msgbuf.c
new file mode 100644
index 0000000..4febdef
--- /dev/null
+++ b/sys/kern/subr_msgbuf.c
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 2003 Ian Dowse. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Generic message buffer support routines.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/msgbuf.h>
+
+/* Read/write sequence numbers are modulo a multiple of the buffer size. */
+#define SEQMOD(size) ((size) * 16)
+
+static u_int msgbuf_cksum(struct msgbuf *mbp);
+
+/*
+ * Initialize a message buffer of the specified size at the specified
+ * location. This also zeros the buffer area.
+ */
+void
+msgbuf_init(struct msgbuf *mbp, void *ptr, int size)
+{
+
+ mbp->msg_ptr = ptr;
+ mbp->msg_size = size;
+ mbp->msg_seqmod = SEQMOD(size);
+ msgbuf_clear(mbp);
+ mbp->msg_magic = MSG_MAGIC;
+}
+
+/*
+ * Reinitialize a message buffer, retaining its previous contents if
+ * the size and checksum are correct. If the old contents cannot be
+ * recovered, the message buffer is cleared.
+ */
+void
+msgbuf_reinit(struct msgbuf *mbp, void *ptr, int size)
+{
+ u_int cksum;
+
+ if (mbp->msg_magic != MSG_MAGIC || mbp->msg_size != size) {
+ msgbuf_init(mbp, ptr, size);
+ return;
+ }
+ mbp->msg_seqmod = SEQMOD(size);
+ mbp->msg_wseq = MSGBUF_SEQNORM(mbp, mbp->msg_wseq);
+ mbp->msg_rseq = MSGBUF_SEQNORM(mbp, mbp->msg_rseq);
+ mbp->msg_ptr = ptr;
+ cksum = msgbuf_cksum(mbp);
+ if (cksum != mbp->msg_cksum) {
+ printf("msgbuf cksum mismatch (read %x, calc %x)\n",
+ mbp->msg_cksum, cksum);
+ msgbuf_clear(mbp);
+ }
+}
+
+/*
+ * Clear the message buffer.
+ */
+void
+msgbuf_clear(struct msgbuf *mbp)
+{
+
+ bzero(mbp->msg_ptr, mbp->msg_size);
+ mbp->msg_wseq = 0;
+ mbp->msg_rseq = 0;
+ mbp->msg_cksum = 0;
+}
+
+/*
+ * Get a count of the number of unread characters in the message buffer.
+ */
+int
+msgbuf_getcount(struct msgbuf *mbp)
+{
+ u_int len;
+
+ len = MSGBUF_SEQSUB(mbp, mbp->msg_wseq, mbp->msg_rseq);
+ if (len > mbp->msg_size)
+ len = mbp->msg_size;
+ return (len);
+}
+
+/*
+ * Append a character to a message buffer. This function can be
+ * considered fully reentrant so long as the number of concurrent
+ * callers is less than the number of characters in the buffer.
+ * However, the message buffer is only guaranteed to be consistent
+ * for reading when there are no callers in this function.
+ */
+void
+msgbuf_addchar(struct msgbuf *mbp, int c)
+{
+ u_int new_seq, pos, seq;
+
+ do {
+ seq = mbp->msg_wseq;
+ new_seq = MSGBUF_SEQNORM(mbp, seq + 1);
+ } while (atomic_cmpset_rel_int(&mbp->msg_wseq, seq, new_seq) == 0);
+ pos = MSGBUF_SEQ_TO_POS(mbp, seq);
+ atomic_add_int(&mbp->msg_cksum, (u_int)(u_char)c -
+ (u_int)(u_char)mbp->msg_ptr[pos]);
+ mbp->msg_ptr[pos] = c;
+}
+
+/*
+ * Read and mark as read a character from a message buffer.
+ * Returns the character, or -1 if no characters are available.
+ */
+int
+msgbuf_getchar(struct msgbuf *mbp)
+{
+ u_int len, wseq;
+ int c;
+
+ wseq = mbp->msg_wseq;
+ len = MSGBUF_SEQSUB(mbp, wseq, mbp->msg_rseq);
+ if (len == 0)
+ return (-1);
+ if (len > mbp->msg_size)
+ mbp->msg_rseq = MSGBUF_SEQNORM(mbp, wseq - mbp->msg_size);
+ c = (u_char)mbp->msg_ptr[MSGBUF_SEQ_TO_POS(mbp, mbp->msg_rseq)];
+ mbp->msg_rseq = MSGBUF_SEQNORM(mbp, mbp->msg_rseq + 1);
+ return (c);
+}
+
+/*
+ * Read and mark as read a number of characters from a message buffer.
+ * Returns the number of characters that were placed in `buf'.
+ */
+int
+msgbuf_getbytes(struct msgbuf *mbp, char *buf, int buflen)
+{
+ u_int len, pos, wseq;
+
+ wseq = mbp->msg_wseq;
+ len = MSGBUF_SEQSUB(mbp, wseq, mbp->msg_rseq);
+ if (len == 0)
+ return (0);
+ if (len > mbp->msg_size) {
+ mbp->msg_rseq = MSGBUF_SEQNORM(mbp, wseq - mbp->msg_size);
+ len = mbp->msg_size;
+ }
+ pos = MSGBUF_SEQ_TO_POS(mbp, mbp->msg_rseq);
+ len = min(len, mbp->msg_size - pos);
+ len = min(len, (u_int)buflen);
+
+ bcopy(&mbp->msg_ptr[pos], buf, len);
+ mbp->msg_rseq = MSGBUF_SEQNORM(mbp, mbp->msg_rseq + len);
+ return (len);
+}
+
+/*
+ * Peek at the full contents of a message buffer without marking any
+ * data as read. `seqp' should point to an unsigned integer that
+ * msgbuf_peekbytes() can use to retain state between calls so that
+ * the whole message buffer can be read in multiple short reads.
+ * To initialise this variable to the start of the message buffer,
+ * call msgbuf_peekbytes() with a NULL `buf' parameter.
+ *
+ * Returns the number of characters that were placed in `buf'.
+ */
+int
+msgbuf_peekbytes(struct msgbuf *mbp, char *buf, int buflen, u_int *seqp)
+{
+ u_int len, pos, wseq;
+
+ if (buf == NULL) {
+ /* Just initialise *seqp. */
+ *seqp = MSGBUF_SEQNORM(mbp, mbp->msg_wseq - mbp->msg_size);
+ return (0);
+ }
+
+ wseq = mbp->msg_wseq;
+ len = MSGBUF_SEQSUB(mbp, wseq, *seqp);
+ if (len == 0)
+ return (0);
+ if (len > mbp->msg_size) {
+ *seqp = MSGBUF_SEQNORM(mbp, wseq - mbp->msg_size);
+ len = mbp->msg_size;
+ }
+ pos = MSGBUF_SEQ_TO_POS(mbp, *seqp);
+ len = min(len, mbp->msg_size - pos);
+ len = min(len, (u_int)buflen);
+ bcopy(&mbp->msg_ptr[MSGBUF_SEQ_TO_POS(mbp, *seqp)], buf, len);
+ *seqp = MSGBUF_SEQNORM(mbp, *seqp + len);
+ return (len);
+}
+
+/*
+ * Compute the checksum for the complete message buffer contents.
+ */
+static u_int
+msgbuf_cksum(struct msgbuf *mbp)
+{
+ u_int i, sum;
+
+ sum = 0;
+ for (i = 0; i < mbp->msg_size; i++)
+ sum += (u_char)mbp->msg_ptr[i];
+ return (sum);
+}
+
+/*
+ * Copy from one message buffer to another.
+ */
+void
+msgbuf_copy(struct msgbuf *src, struct msgbuf *dst)
+{
+ int c;
+
+ while ((c = msgbuf_getchar(src)) >= 0)
+ msgbuf_addchar(dst, c);
+}
diff --git a/sys/kern/subr_prf.c b/sys/kern/subr_prf.c
index ed2c280..d564cf6 100644
--- a/sys/kern/subr_prf.c
+++ b/sys/kern/subr_prf.c
@@ -93,8 +93,6 @@ struct tty *constty; /* pointer to console "window" tty */
static void (*v_putc)(int) = cnputc; /* routine to putc on virtual console */
static void msglogchar(int c, int pri);
-static void msgaddchar(int c, void *dummy);
-static u_int msgbufcksum(char *cp, size_t size, u_int cksum);
static void putchar(int ch, void *arg);
static char *ksprintn(char *nbuf, uintmax_t num, int base, int *len);
static void snprintf_func(int ch, void *arg);
@@ -788,16 +786,16 @@ msglogchar(int c, int pri)
return;
if (pri != -1 && pri != lastpri) {
if (dangling) {
- msgaddchar('\n', NULL);
+ msgbuf_addchar(msgbufp, '\n');
dangling = 0;
}
- msgaddchar('<', NULL);
+ msgbuf_addchar(msgbufp, '<');
for (p = ksprintn(nbuf, (uintmax_t)pri, 10, NULL); *p;)
- msgaddchar(*p--, NULL);
- msgaddchar('>', NULL);
+ msgbuf_addchar(msgbufp, *p--);
+ msgbuf_addchar(msgbufp, '>');
lastpri = pri;
}
- msgaddchar(c, NULL);
+ msgbuf_addchar(msgbufp, c);
if (c == '\n') {
dangling = 0;
lastpri = -1;
@@ -806,41 +804,6 @@ msglogchar(int c, int pri)
}
}
-/*
- * Put char in log buffer
- */
-static void
-msgaddchar(int c, void *dummy)
-{
- struct msgbuf *mbp;
-
- if (!msgbufmapped)
- return;
- mbp = msgbufp;
- mbp->msg_cksum += (u_char)c - (u_char)mbp->msg_ptr[mbp->msg_bufx];
- mbp->msg_ptr[mbp->msg_bufx++] = c;
- if (mbp->msg_bufx >= mbp->msg_size)
- mbp->msg_bufx = 0;
- /* If the buffer is full, keep the most recent data. */
- if (mbp->msg_bufr == mbp->msg_bufx) {
- if (++mbp->msg_bufr >= mbp->msg_size)
- mbp->msg_bufr = 0;
- }
-}
-
-static void
-msgbufcopy(struct msgbuf *oldp)
-{
- int pos;
-
- pos = oldp->msg_bufr;
- while (pos != oldp->msg_bufx) {
- msglogchar(oldp->msg_ptr[pos], -1);
- if (++pos >= oldp->msg_size)
- pos = 0;
- }
-}
-
void
msgbufinit(void *ptr, int size)
{
@@ -849,39 +812,14 @@ msgbufinit(void *ptr, int size)
size -= sizeof(*msgbufp);
cp = (char *)ptr;
- msgbufp = (struct msgbuf *) (cp + size);
- if (msgbufp->msg_magic != MSG_MAGIC || msgbufp->msg_size != size ||
- msgbufp->msg_bufx >= size || msgbufp->msg_bufx < 0 ||
- msgbufp->msg_bufr >= size || msgbufp->msg_bufr < 0 ||
- msgbufcksum(cp, size, msgbufp->msg_cksum) != msgbufp->msg_cksum) {
- bzero(cp, size);
- bzero(msgbufp, sizeof(*msgbufp));
- msgbufp->msg_magic = MSG_MAGIC;
- msgbufp->msg_size = size;
- }
- msgbufp->msg_ptr = cp;
+ msgbufp = (struct msgbuf *)(cp + size);
+ msgbuf_reinit(msgbufp, cp, size);
if (msgbufmapped && oldp != msgbufp)
- msgbufcopy(oldp);
+ msgbuf_copy(oldp, msgbufp);
msgbufmapped = 1;
oldp = msgbufp;
}
-static u_int
-msgbufcksum(char *cp, size_t size, u_int cksum)
-{
- u_int sum;
- int i;
-
- sum = 0;
- for (i = 0; i < size; i++)
- sum += (u_char)cp[i];
- if (sum != cksum)
- printf("msgbuf cksum mismatch (read %x, calc %x)\n", cksum,
- sum);
-
- return (sum);
-}
-
SYSCTL_DECL(_security_bsd);
static int unprivileged_read_msgbuf = 1;
@@ -893,7 +831,9 @@ SYSCTL_INT(_security_bsd, OID_AUTO, unprivileged_read_msgbuf,
static int
sysctl_kern_msgbuf(SYSCTL_HANDLER_ARGS)
{
- int error;
+ char buf[128];
+ u_int seq;
+ int error, len;
if (!unprivileged_read_msgbuf) {
error = suser(req->td);
@@ -901,25 +841,20 @@ sysctl_kern_msgbuf(SYSCTL_HANDLER_ARGS)
return (error);
}
- /*
- * Unwind the buffer, so that it's linear (possibly starting with
- * some initial nulls).
- */
- error = sysctl_handle_opaque(oidp, msgbufp->msg_ptr + msgbufp->msg_bufx,
- msgbufp->msg_size - msgbufp->msg_bufx, req);
- if (error)
- return (error);
- if (msgbufp->msg_bufx > 0) {
- error = sysctl_handle_opaque(oidp, msgbufp->msg_ptr,
- msgbufp->msg_bufx, req);
+ /* Read the whole buffer, one chunk at a time. */
+ msgbuf_peekbytes(msgbufp, NULL, 0, &seq);
+ while ((len = msgbuf_peekbytes(msgbufp, buf, sizeof(buf), &seq)) > 0) {
+ error = sysctl_handle_opaque(oidp, buf, len, req);
+ if (error)
+ return (error);
}
- return (error);
+ return (0);
}
SYSCTL_PROC(_kern, OID_AUTO, msgbuf, CTLTYPE_STRING | CTLFLAG_RD,
0, 0, sysctl_kern_msgbuf, "A", "Contents of kernel message buffer");
-static int msgbuf_clear;
+static int msgbuf_clearflag;
static int
sysctl_kern_msgbuf_clear(SYSCTL_HANDLER_ARGS)
@@ -927,17 +862,14 @@ sysctl_kern_msgbuf_clear(SYSCTL_HANDLER_ARGS)
int error;
error = sysctl_handle_int(oidp, oidp->oid_arg1, oidp->oid_arg2, req);
if (!error && req->newptr) {
- /* Clear the buffer and reset write pointer */
- bzero(msgbufp->msg_ptr, msgbufp->msg_size);
- msgbufp->msg_bufr = msgbufp->msg_bufx = 0;
- msgbufp->msg_cksum = 0;
- msgbuf_clear = 0;
+ msgbuf_clear(msgbufp);
+ msgbuf_clearflag = 0;
}
return (error);
}
SYSCTL_PROC(_kern, OID_AUTO, msgbuf_clear,
- CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_SECURE, &msgbuf_clear, 0,
+ CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_SECURE, &msgbuf_clearflag, 0,
sysctl_kern_msgbuf_clear, "I", "Clear kernel message buffer");
#ifdef DDB
@@ -951,11 +883,11 @@ DB_SHOW_COMMAND(msgbuf, db_show_msgbuf)
return;
}
db_printf("msgbufp = %p\n", msgbufp);
- db_printf("magic = %x, size = %d, r= %d, w = %d, ptr = %p, cksum= %d\n",
- msgbufp->msg_magic, msgbufp->msg_size, msgbufp->msg_bufr,
- msgbufp->msg_bufx, msgbufp->msg_ptr, msgbufp->msg_cksum);
+ db_printf("magic = %x, size = %d, r= %u, w = %u, ptr = %p, cksum= %u\n",
+ msgbufp->msg_magic, msgbufp->msg_size, msgbufp->msg_rseq,
+ msgbufp->msg_wseq, msgbufp->msg_ptr, msgbufp->msg_cksum);
for (i = 0; i < msgbufp->msg_size; i++) {
- j = (i + msgbufp->msg_bufr) % msgbufp->msg_size;
+ j = MSGBUF_SEQ_TO_POS(msgbufp, i + msgbufp->msg_rseq);
db_printf("%c", msgbufp->msg_ptr[j]);
}
db_printf("\n");
diff --git a/sys/sys/msgbuf.h b/sys/sys/msgbuf.h
index 22bd161..b92ccc8 100644
--- a/sys/sys/msgbuf.h
+++ b/sys/sys/msgbuf.h
@@ -38,19 +38,37 @@
#define _SYS_MSGBUF_H_
struct msgbuf {
+ char *msg_ptr; /* pointer to buffer */
#define MSG_MAGIC 0x063062
u_int msg_magic;
- int msg_size; /* size of buffer area */
- int msg_bufx; /* write pointer */
- int msg_bufr; /* read pointer */
- char *msg_ptr; /* pointer to buffer */
+ u_int msg_size; /* size of buffer area */
+ u_int msg_wseq; /* write sequence number */
+ u_int msg_rseq; /* read sequence number */
u_int msg_cksum; /* checksum of contents */
+ u_int msg_seqmod; /* range for sequence numbers */
};
+/* Normalise a sequence number or a difference between sequence numbers */
+#define MSGBUF_SEQNORM(mbp, seq) (((seq) + (mbp)->msg_seqmod) % \
+ (mbp)->msg_seqmod)
+#define MSGBUF_SEQ_TO_POS(mbp, seq) ((seq) % (mbp)->msg_size)
+/* Subtract sequence numbers, but note that only positive values result. */
+#define MSGBUF_SEQSUB(mbp, seq1, seq2) (MSGBUF_SEQNORM((mbp), (seq1) - (seq2)))
+
#ifdef _KERNEL
extern int msgbuftrigger;
extern struct msgbuf *msgbufp;
void msgbufinit(void *ptr, int size);
+void msgbuf_addchar(struct msgbuf *mbp, int c);
+void msgbuf_clear(struct msgbuf *mbp);
+void msgbuf_copy(struct msgbuf *src, struct msgbuf *dst);
+int msgbuf_getbytes(struct msgbuf *mbp, char *buf, int buflen);
+int msgbuf_getchar(struct msgbuf *mbp);
+int msgbuf_getcount(struct msgbuf *mbp);
+void msgbuf_init(struct msgbuf *mbp, void *ptr, int size);
+void msgbuf_reinit(struct msgbuf *mbp, void *ptr, int size);
+int msgbuf_peekbytes(struct msgbuf *mbp, char *buf, int buflen,
+ u_int *seqp);
#if !defined(MSGBUF_SIZE)
#define MSGBUF_SIZE 32768
OpenPOWER on IntegriCloud