subr_msgbuf.c revision 222537
1139804Simp/*-
2116660Siedowse * Copyright (c) 2003 Ian Dowse.  All rights reserved.
3116660Siedowse *
4116660Siedowse * Redistribution and use in source and binary forms, with or without
5116660Siedowse * modification, are permitted provided that the following conditions
6116660Siedowse * are met:
7116660Siedowse * 1. Redistributions of source code must retain the above copyright
8116660Siedowse *    notice, this list of conditions and the following disclaimer.
9116660Siedowse * 2. Redistributions in binary form must reproduce the above copyright
10116660Siedowse *    notice, this list of conditions and the following disclaimer in the
11116660Siedowse *    documentation and/or other materials provided with the distribution.
12116660Siedowse *
13116660Siedowse * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14116660Siedowse * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15116660Siedowse * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16116660Siedowse * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17116660Siedowse * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18116660Siedowse * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19116660Siedowse * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20116660Siedowse * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21116660Siedowse * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22116660Siedowse * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23116660Siedowse * SUCH DAMAGE.
24116660Siedowse *
25116660Siedowse * $FreeBSD: head/sys/kern/subr_msgbuf.c 222537 2011-05-31 17:29:58Z ken $
26116660Siedowse */
27116660Siedowse
28116660Siedowse/*
29116660Siedowse * Generic message buffer support routines.
30116660Siedowse */
31116660Siedowse
32116660Siedowse#include <sys/param.h>
33116660Siedowse#include <sys/systm.h>
34222537Sken#include <sys/lock.h>
35222537Sken#include <sys/mutex.h>
36116660Siedowse#include <sys/msgbuf.h>
37116660Siedowse
38222537Sken/*
39222537Sken * Maximum number conversion buffer length: uintmax_t in base 2, plus <>
40222537Sken * around the priority, and a terminating NUL.
41222537Sken */
42222537Sken#define	MAXPRIBUF	(sizeof(intmax_t) * NBBY + 3)
43222537Sken
44116660Siedowse/* Read/write sequence numbers are modulo a multiple of the buffer size. */
45116660Siedowse#define SEQMOD(size) ((size) * 16)
46116660Siedowse
47116660Siedowsestatic u_int msgbuf_cksum(struct msgbuf *mbp);
48116660Siedowse
49116660Siedowse/*
50116660Siedowse * Initialize a message buffer of the specified size at the specified
51116660Siedowse * location. This also zeros the buffer area.
52116660Siedowse */
53116660Siedowsevoid
54116660Siedowsemsgbuf_init(struct msgbuf *mbp, void *ptr, int size)
55116660Siedowse{
56116660Siedowse
57116660Siedowse	mbp->msg_ptr = ptr;
58116660Siedowse	mbp->msg_size = size;
59116660Siedowse	mbp->msg_seqmod = SEQMOD(size);
60116660Siedowse	msgbuf_clear(mbp);
61116660Siedowse	mbp->msg_magic = MSG_MAGIC;
62222537Sken	mbp->msg_lastpri = -1;
63222537Sken	mbp->msg_needsnl = 0;
64222537Sken	mtx_init(&mbp->msg_lock, "msgbuf", NULL, MTX_SPIN);
65116660Siedowse}
66116660Siedowse
67116660Siedowse/*
68116660Siedowse * Reinitialize a message buffer, retaining its previous contents if
69116660Siedowse * the size and checksum are correct. If the old contents cannot be
70116660Siedowse * recovered, the message buffer is cleared.
71116660Siedowse */
72116660Siedowsevoid
73116660Siedowsemsgbuf_reinit(struct msgbuf *mbp, void *ptr, int size)
74116660Siedowse{
75116660Siedowse	u_int cksum;
76116660Siedowse
77116660Siedowse	if (mbp->msg_magic != MSG_MAGIC || mbp->msg_size != size) {
78116660Siedowse		msgbuf_init(mbp, ptr, size);
79116660Siedowse		return;
80116660Siedowse	}
81116660Siedowse	mbp->msg_seqmod = SEQMOD(size);
82116660Siedowse	mbp->msg_wseq = MSGBUF_SEQNORM(mbp, mbp->msg_wseq);
83116660Siedowse	mbp->msg_rseq = MSGBUF_SEQNORM(mbp, mbp->msg_rseq);
84116660Siedowse        mbp->msg_ptr = ptr;
85116660Siedowse	cksum = msgbuf_cksum(mbp);
86116660Siedowse	if (cksum != mbp->msg_cksum) {
87119765Sphk		if (bootverbose) {
88119765Sphk			printf("msgbuf cksum mismatch (read %x, calc %x)\n",
89119765Sphk			    mbp->msg_cksum, cksum);
90119765Sphk			printf("Old msgbuf not recovered\n");
91119765Sphk		}
92116660Siedowse		msgbuf_clear(mbp);
93116660Siedowse	}
94222537Sken
95222537Sken	mbp->msg_lastpri = -1;
96222537Sken	/* Assume that the old message buffer didn't end in a newline. */
97222537Sken	mbp->msg_needsnl = 1;
98222537Sken	mtx_init(&mbp->msg_lock, "msgbuf", NULL, MTX_SPIN);
99116660Siedowse}
100116660Siedowse
101116660Siedowse/*
102116660Siedowse * Clear the message buffer.
103116660Siedowse */
104116660Siedowsevoid
105116660Siedowsemsgbuf_clear(struct msgbuf *mbp)
106116660Siedowse{
107116660Siedowse
108116660Siedowse	bzero(mbp->msg_ptr, mbp->msg_size);
109116660Siedowse	mbp->msg_wseq = 0;
110116660Siedowse	mbp->msg_rseq = 0;
111116660Siedowse	mbp->msg_cksum = 0;
112116660Siedowse}
113116660Siedowse
114116660Siedowse/*
115116660Siedowse * Get a count of the number of unread characters in the message buffer.
116116660Siedowse */
117116660Siedowseint
118116660Siedowsemsgbuf_getcount(struct msgbuf *mbp)
119116660Siedowse{
120116660Siedowse	u_int len;
121116660Siedowse
122116660Siedowse	len = MSGBUF_SEQSUB(mbp, mbp->msg_wseq, mbp->msg_rseq);
123116660Siedowse	if (len > mbp->msg_size)
124116660Siedowse		len = mbp->msg_size;
125116660Siedowse	return (len);
126116660Siedowse}
127116660Siedowse
128116660Siedowse/*
129222537Sken * Add a character into the message buffer, and update the checksum and
130222537Sken * sequence number.
131222537Sken *
132222537Sken * The caller should hold the message buffer spinlock.
133116660Siedowse */
134222537Skenstatic inline void
135222537Skenmsgbuf_do_addchar(struct msgbuf *mbp, u_int *seq, int c)
136222537Sken{
137222537Sken	u_int pos;
138222537Sken
139222537Sken	/* Make sure we properly wrap the sequence number. */
140222537Sken	pos = MSGBUF_SEQ_TO_POS(mbp, *seq);
141222537Sken
142222537Sken	mbp->msg_cksum += (u_int)c -
143222537Sken	    (u_int)(u_char)mbp->msg_ptr[pos];
144222537Sken
145222537Sken	mbp->msg_ptr[pos] = c;
146222537Sken
147222537Sken	*seq = MSGBUF_SEQNORM(mbp, *seq + 1);
148222537Sken}
149222537Sken
150222537Sken/*
151222537Sken * Append a character to a message buffer.
152222537Sken */
153116660Siedowsevoid
154116660Siedowsemsgbuf_addchar(struct msgbuf *mbp, int c)
155116660Siedowse{
156222537Sken	mtx_lock_spin(&mbp->msg_lock);
157116660Siedowse
158222537Sken	msgbuf_do_addchar(mbp, &mbp->msg_wseq, c);
159222537Sken
160222537Sken	mtx_unlock_spin(&mbp->msg_lock);
161116660Siedowse}
162116660Siedowse
163116660Siedowse/*
164222537Sken * Append a NUL-terminated string with a priority to a message buffer.
165222537Sken * Filter carriage returns if the caller requests it.
166222537Sken *
167222537Sken * XXX The carriage return filtering behavior is present in the
168222537Sken * msglogchar() API, however testing has shown that we don't seem to send
169222537Sken * carriage returns down this path.  So do we still need it?
170222537Sken */
171222537Skenvoid
172222537Skenmsgbuf_addstr(struct msgbuf *mbp, int pri, char *str, int filter_cr)
173222537Sken{
174222537Sken	u_int seq;
175222537Sken	size_t len, prefix_len;
176222537Sken	char prefix[MAXPRIBUF];
177222537Sken	int nl, i;
178222537Sken
179222537Sken	len = strlen(str);
180222537Sken	prefix_len = 0;
181222537Sken	nl = 0;
182222537Sken
183222537Sken	/* If we have a zero-length string, no need to do anything. */
184222537Sken	if (len == 0)
185222537Sken		return;
186222537Sken
187222537Sken	mtx_lock_spin(&mbp->msg_lock);
188222537Sken
189222537Sken	/*
190222537Sken	 * If this is true, we may need to insert a new priority sequence,
191222537Sken	 * so prepare the prefix.
192222537Sken	 */
193222537Sken	if (pri != -1)
194222537Sken		prefix_len = sprintf(prefix, "<%d>", pri);
195222537Sken
196222537Sken	/*
197222537Sken	 * Starting write sequence number.
198222537Sken	 */
199222537Sken	seq = mbp->msg_wseq;
200222537Sken
201222537Sken	/*
202222537Sken	 * Whenever there is a change in priority, we have to insert a
203222537Sken	 * newline, and a priority prefix if the priority is not -1.  Here
204222537Sken	 * we detect whether there was a priority change, and whether we
205222537Sken	 * did not end with a newline.  If that is the case, we need to
206222537Sken	 * insert a newline before this string.
207222537Sken	 */
208222537Sken	if (mbp->msg_lastpri != pri && mbp->msg_needsnl != 0) {
209222537Sken
210222537Sken		msgbuf_do_addchar(mbp, &seq, '\n');
211222537Sken		mbp->msg_needsnl = 0;
212222537Sken	}
213222537Sken
214222537Sken	for (i = 0; i < len; i++) {
215222537Sken		/*
216222537Sken		 * If we just had a newline, and the priority is not -1
217222537Sken		 * (and therefore prefix_len != 0), then we need a priority
218222537Sken		 * prefix for this line.
219222537Sken		 */
220222537Sken		if (mbp->msg_needsnl == 0 && prefix_len != 0) {
221222537Sken			int j;
222222537Sken
223222537Sken			for (j = 0; j < prefix_len; j++)
224222537Sken				msgbuf_do_addchar(mbp, &seq, prefix[j]);
225222537Sken		}
226222537Sken
227222537Sken		/*
228222537Sken		 * Don't copy carriage returns if the caller requested
229222537Sken		 * filtering.
230222537Sken		 *
231222537Sken		 * XXX This matches the behavior of msglogchar(), but is it
232222537Sken		 * necessary?  Testing has shown that we don't seem to get
233222537Sken		 * carriage returns here.
234222537Sken		 */
235222537Sken		if ((filter_cr != 0) && (str[i] == '\r'))
236222537Sken			continue;
237222537Sken
238222537Sken		/*
239222537Sken		 * Clear this flag if we see a newline.  This affects whether
240222537Sken		 * we need to insert a new prefix or insert a newline later.
241222537Sken		 */
242222537Sken		if (str[i] == '\n')
243222537Sken			mbp->msg_needsnl = 0;
244222537Sken		else
245222537Sken			mbp->msg_needsnl = 1;
246222537Sken
247222537Sken		msgbuf_do_addchar(mbp, &seq, str[i]);
248222537Sken	}
249222537Sken	/*
250222537Sken	 * Update the write sequence number for the actual number of
251222537Sken	 * characters we put in the message buffer.  (Depends on whether
252222537Sken	 * carriage returns are filtered.)
253222537Sken	 */
254222537Sken	mbp->msg_wseq = seq;
255222537Sken
256222537Sken	/*
257222537Sken	 * Set the last priority.
258222537Sken	 */
259222537Sken	mbp->msg_lastpri = pri;
260222537Sken
261222537Sken	mtx_unlock_spin(&mbp->msg_lock);
262222537Sken
263222537Sken}
264222537Sken
265222537Sken/*
266116660Siedowse * Read and mark as read a character from a message buffer.
267116660Siedowse * Returns the character, or -1 if no characters are available.
268116660Siedowse */
269116660Siedowseint
270116660Siedowsemsgbuf_getchar(struct msgbuf *mbp)
271116660Siedowse{
272116660Siedowse	u_int len, wseq;
273116660Siedowse	int c;
274116660Siedowse
275222537Sken	mtx_lock_spin(&mbp->msg_lock);
276222537Sken
277116660Siedowse	wseq = mbp->msg_wseq;
278116660Siedowse	len = MSGBUF_SEQSUB(mbp, wseq, mbp->msg_rseq);
279222537Sken	if (len == 0) {
280222537Sken		mtx_unlock_spin(&mbp->msg_lock);
281116660Siedowse		return (-1);
282222537Sken	}
283116660Siedowse	if (len > mbp->msg_size)
284116660Siedowse		mbp->msg_rseq = MSGBUF_SEQNORM(mbp, wseq - mbp->msg_size);
285116660Siedowse	c = (u_char)mbp->msg_ptr[MSGBUF_SEQ_TO_POS(mbp, mbp->msg_rseq)];
286116660Siedowse	mbp->msg_rseq = MSGBUF_SEQNORM(mbp, mbp->msg_rseq + 1);
287222537Sken
288222537Sken	mtx_unlock_spin(&mbp->msg_lock);
289222537Sken
290116660Siedowse	return (c);
291116660Siedowse}
292116660Siedowse
293116660Siedowse/*
294116660Siedowse * Read and mark as read a number of characters from a message buffer.
295116660Siedowse * Returns the number of characters that were placed in `buf'.
296116660Siedowse */
297116660Siedowseint
298116660Siedowsemsgbuf_getbytes(struct msgbuf *mbp, char *buf, int buflen)
299116660Siedowse{
300116660Siedowse	u_int len, pos, wseq;
301116660Siedowse
302222537Sken	mtx_lock_spin(&mbp->msg_lock);
303222537Sken
304116660Siedowse	wseq = mbp->msg_wseq;
305116660Siedowse	len = MSGBUF_SEQSUB(mbp, wseq, mbp->msg_rseq);
306222537Sken	if (len == 0) {
307222537Sken		mtx_unlock_spin(&mbp->msg_lock);
308116660Siedowse		return (0);
309222537Sken	}
310116660Siedowse	if (len > mbp->msg_size) {
311116660Siedowse		mbp->msg_rseq = MSGBUF_SEQNORM(mbp, wseq - mbp->msg_size);
312116660Siedowse		len = mbp->msg_size;
313116660Siedowse	}
314116660Siedowse	pos = MSGBUF_SEQ_TO_POS(mbp, mbp->msg_rseq);
315116660Siedowse	len = min(len, mbp->msg_size - pos);
316116660Siedowse	len = min(len, (u_int)buflen);
317116660Siedowse
318116660Siedowse	bcopy(&mbp->msg_ptr[pos], buf, len);
319116660Siedowse	mbp->msg_rseq = MSGBUF_SEQNORM(mbp, mbp->msg_rseq + len);
320222537Sken
321222537Sken	mtx_unlock_spin(&mbp->msg_lock);
322222537Sken
323116660Siedowse	return (len);
324116660Siedowse}
325116660Siedowse
326116660Siedowse/*
327116660Siedowse * Peek at the full contents of a message buffer without marking any
328116660Siedowse * data as read. `seqp' should point to an unsigned integer that
329116660Siedowse * msgbuf_peekbytes() can use to retain state between calls so that
330116660Siedowse * the whole message buffer can be read in multiple short reads.
331116660Siedowse * To initialise this variable to the start of the message buffer,
332116660Siedowse * call msgbuf_peekbytes() with a NULL `buf' parameter.
333116660Siedowse *
334116660Siedowse * Returns the number of characters that were placed in `buf'.
335116660Siedowse */
336116660Siedowseint
337116660Siedowsemsgbuf_peekbytes(struct msgbuf *mbp, char *buf, int buflen, u_int *seqp)
338116660Siedowse{
339116660Siedowse	u_int len, pos, wseq;
340116660Siedowse
341222537Sken	mtx_lock_spin(&mbp->msg_lock);
342222537Sken
343116660Siedowse	if (buf == NULL) {
344116660Siedowse		/* Just initialise *seqp. */
345116660Siedowse		*seqp = MSGBUF_SEQNORM(mbp, mbp->msg_wseq - mbp->msg_size);
346222537Sken		mtx_unlock_spin(&mbp->msg_lock);
347116660Siedowse		return (0);
348116660Siedowse	}
349116660Siedowse
350116660Siedowse	wseq = mbp->msg_wseq;
351116660Siedowse	len = MSGBUF_SEQSUB(mbp, wseq, *seqp);
352222537Sken	if (len == 0) {
353222537Sken		mtx_unlock_spin(&mbp->msg_lock);
354116660Siedowse		return (0);
355222537Sken	}
356116660Siedowse	if (len > mbp->msg_size) {
357116660Siedowse		*seqp = MSGBUF_SEQNORM(mbp, wseq - mbp->msg_size);
358116660Siedowse		len = mbp->msg_size;
359116660Siedowse	}
360116660Siedowse	pos = MSGBUF_SEQ_TO_POS(mbp, *seqp);
361116660Siedowse	len = min(len, mbp->msg_size - pos);
362116660Siedowse	len = min(len, (u_int)buflen);
363116660Siedowse	bcopy(&mbp->msg_ptr[MSGBUF_SEQ_TO_POS(mbp, *seqp)], buf, len);
364116660Siedowse	*seqp = MSGBUF_SEQNORM(mbp, *seqp + len);
365222537Sken
366222537Sken	mtx_unlock_spin(&mbp->msg_lock);
367222537Sken
368116660Siedowse	return (len);
369116660Siedowse}
370116660Siedowse
371116660Siedowse/*
372116660Siedowse * Compute the checksum for the complete message buffer contents.
373116660Siedowse */
374116660Siedowsestatic u_int
375116660Siedowsemsgbuf_cksum(struct msgbuf *mbp)
376116660Siedowse{
377116660Siedowse	u_int i, sum;
378116660Siedowse
379116660Siedowse	sum = 0;
380116660Siedowse	for (i = 0; i < mbp->msg_size; i++)
381116660Siedowse		sum += (u_char)mbp->msg_ptr[i];
382116660Siedowse	return (sum);
383116660Siedowse}
384116660Siedowse
385116660Siedowse/*
386116660Siedowse * Copy from one message buffer to another.
387116660Siedowse */
388116660Siedowsevoid
389116660Siedowsemsgbuf_copy(struct msgbuf *src, struct msgbuf *dst)
390116660Siedowse{
391116660Siedowse	int c;
392116660Siedowse
393116660Siedowse	while ((c = msgbuf_getchar(src)) >= 0)
394116660Siedowse		msgbuf_addchar(dst, c);
395116660Siedowse}
396