xref: /freebsd/sys/kern/subr_msgbuf.c (revision dce6e6518b85561495cff38a3074a69d29d58a55)
1 /*
2  * Copyright (c) 2003 Ian Dowse.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  *
25  * $FreeBSD$
26  */
27 
28 /*
29  * Generic message buffer support routines.
30  */
31 
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/msgbuf.h>
35 
36 /* Read/write sequence numbers are modulo a multiple of the buffer size. */
37 #define SEQMOD(size) ((size) * 16)
38 
39 static u_int msgbuf_cksum(struct msgbuf *mbp);
40 
41 /*
42  * Initialize a message buffer of the specified size at the specified
43  * location. This also zeros the buffer area.
44  */
45 void
46 msgbuf_init(struct msgbuf *mbp, void *ptr, int size)
47 {
48 
49 	mbp->msg_ptr = ptr;
50 	mbp->msg_size = size;
51 	mbp->msg_seqmod = SEQMOD(size);
52 	msgbuf_clear(mbp);
53 	mbp->msg_magic = MSG_MAGIC;
54 }
55 
56 /*
57  * Reinitialize a message buffer, retaining its previous contents if
58  * the size and checksum are correct. If the old contents cannot be
59  * recovered, the message buffer is cleared.
60  */
61 void
62 msgbuf_reinit(struct msgbuf *mbp, void *ptr, int size)
63 {
64 	u_int cksum;
65 
66 	if (mbp->msg_magic != MSG_MAGIC || mbp->msg_size != size) {
67 		msgbuf_init(mbp, ptr, size);
68 		return;
69 	}
70 	mbp->msg_seqmod = SEQMOD(size);
71 	mbp->msg_wseq = MSGBUF_SEQNORM(mbp, mbp->msg_wseq);
72 	mbp->msg_rseq = MSGBUF_SEQNORM(mbp, mbp->msg_rseq);
73         mbp->msg_ptr = ptr;
74 	cksum = msgbuf_cksum(mbp);
75 	if (cksum != mbp->msg_cksum) {
76 		printf("msgbuf cksum mismatch (read %x, calc %x)\n",
77 		    mbp->msg_cksum, cksum);
78 		msgbuf_clear(mbp);
79 	}
80 }
81 
82 /*
83  * Clear the message buffer.
84  */
85 void
86 msgbuf_clear(struct msgbuf *mbp)
87 {
88 
89 	bzero(mbp->msg_ptr, mbp->msg_size);
90 	mbp->msg_wseq = 0;
91 	mbp->msg_rseq = 0;
92 	mbp->msg_cksum = 0;
93 }
94 
95 /*
96  * Get a count of the number of unread characters in the message buffer.
97  */
98 int
99 msgbuf_getcount(struct msgbuf *mbp)
100 {
101 	u_int len;
102 
103 	len = MSGBUF_SEQSUB(mbp, mbp->msg_wseq, mbp->msg_rseq);
104 	if (len > mbp->msg_size)
105 		len = mbp->msg_size;
106 	return (len);
107 }
108 
109 /*
110  * Append a character to a message buffer.  This function can be
111  * considered fully reentrant so long as the number of concurrent
112  * callers is less than the number of characters in the buffer.
113  * However, the message buffer is only guaranteed to be consistent
114  * for reading when there are no callers in this function.
115  */
116 void
117 msgbuf_addchar(struct msgbuf *mbp, int c)
118 {
119 	u_int new_seq, pos, seq;
120 
121 	do {
122 		seq = mbp->msg_wseq;
123 		new_seq = MSGBUF_SEQNORM(mbp, seq + 1);
124 	} while (atomic_cmpset_rel_int(&mbp->msg_wseq, seq, new_seq) == 0);
125 	pos = MSGBUF_SEQ_TO_POS(mbp, seq);
126 	atomic_add_int(&mbp->msg_cksum, (u_int)(u_char)c -
127 	    (u_int)(u_char)mbp->msg_ptr[pos]);
128 	mbp->msg_ptr[pos] = c;
129 }
130 
131 /*
132  * Read and mark as read a character from a message buffer.
133  * Returns the character, or -1 if no characters are available.
134  */
135 int
136 msgbuf_getchar(struct msgbuf *mbp)
137 {
138 	u_int len, wseq;
139 	int c;
140 
141 	wseq = mbp->msg_wseq;
142 	len = MSGBUF_SEQSUB(mbp, wseq, mbp->msg_rseq);
143 	if (len == 0)
144 		return (-1);
145 	if (len > mbp->msg_size)
146 		mbp->msg_rseq = MSGBUF_SEQNORM(mbp, wseq - mbp->msg_size);
147 	c = (u_char)mbp->msg_ptr[MSGBUF_SEQ_TO_POS(mbp, mbp->msg_rseq)];
148 	mbp->msg_rseq = MSGBUF_SEQNORM(mbp, mbp->msg_rseq + 1);
149 	return (c);
150 }
151 
152 /*
153  * Read and mark as read a number of characters from a message buffer.
154  * Returns the number of characters that were placed in `buf'.
155  */
156 int
157 msgbuf_getbytes(struct msgbuf *mbp, char *buf, int buflen)
158 {
159 	u_int len, pos, wseq;
160 
161 	wseq = mbp->msg_wseq;
162 	len = MSGBUF_SEQSUB(mbp, wseq, mbp->msg_rseq);
163 	if (len == 0)
164 		return (0);
165 	if (len > mbp->msg_size) {
166 		mbp->msg_rseq = MSGBUF_SEQNORM(mbp, wseq - mbp->msg_size);
167 		len = mbp->msg_size;
168 	}
169 	pos = MSGBUF_SEQ_TO_POS(mbp, mbp->msg_rseq);
170 	len = min(len, mbp->msg_size - pos);
171 	len = min(len, (u_int)buflen);
172 
173 	bcopy(&mbp->msg_ptr[pos], buf, len);
174 	mbp->msg_rseq = MSGBUF_SEQNORM(mbp, mbp->msg_rseq + len);
175 	return (len);
176 }
177 
178 /*
179  * Peek at the full contents of a message buffer without marking any
180  * data as read. `seqp' should point to an unsigned integer that
181  * msgbuf_peekbytes() can use to retain state between calls so that
182  * the whole message buffer can be read in multiple short reads.
183  * To initialise this variable to the start of the message buffer,
184  * call msgbuf_peekbytes() with a NULL `buf' parameter.
185  *
186  * Returns the number of characters that were placed in `buf'.
187  */
188 int
189 msgbuf_peekbytes(struct msgbuf *mbp, char *buf, int buflen, u_int *seqp)
190 {
191 	u_int len, pos, wseq;
192 
193 	if (buf == NULL) {
194 		/* Just initialise *seqp. */
195 		*seqp = MSGBUF_SEQNORM(mbp, mbp->msg_wseq - mbp->msg_size);
196 		return (0);
197 	}
198 
199 	wseq = mbp->msg_wseq;
200 	len = MSGBUF_SEQSUB(mbp, wseq, *seqp);
201 	if (len == 0)
202 		return (0);
203 	if (len > mbp->msg_size) {
204 		*seqp = MSGBUF_SEQNORM(mbp, wseq - mbp->msg_size);
205 		len = mbp->msg_size;
206 	}
207 	pos = MSGBUF_SEQ_TO_POS(mbp, *seqp);
208 	len = min(len, mbp->msg_size - pos);
209 	len = min(len, (u_int)buflen);
210 	bcopy(&mbp->msg_ptr[MSGBUF_SEQ_TO_POS(mbp, *seqp)], buf, len);
211 	*seqp = MSGBUF_SEQNORM(mbp, *seqp + len);
212 	return (len);
213 }
214 
215 /*
216  * Compute the checksum for the complete message buffer contents.
217  */
218 static u_int
219 msgbuf_cksum(struct msgbuf *mbp)
220 {
221 	u_int i, sum;
222 
223 	sum = 0;
224 	for (i = 0; i < mbp->msg_size; i++)
225 		sum += (u_char)mbp->msg_ptr[i];
226 	return (sum);
227 }
228 
229 /*
230  * Copy from one message buffer to another.
231  */
232 void
233 msgbuf_copy(struct msgbuf *src, struct msgbuf *dst)
234 {
235 	int c;
236 
237 	while ((c = msgbuf_getchar(src)) >= 0)
238 		msgbuf_addchar(dst, c);
239 }
240