]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - sys/kern/subr_msgbuf.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / sys / kern / subr_msgbuf.c
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                 if (bootverbose) {
77                         printf("msgbuf cksum mismatch (read %x, calc %x)\n",
78                             mbp->msg_cksum, cksum);
79                         printf("Old msgbuf not recovered\n");
80                 }
81                 msgbuf_clear(mbp);
82         }
83 }
84
85 /*
86  * Clear the message buffer.
87  */
88 void
89 msgbuf_clear(struct msgbuf *mbp)
90 {
91
92         bzero(mbp->msg_ptr, mbp->msg_size);
93         mbp->msg_wseq = 0;
94         mbp->msg_rseq = 0;
95         mbp->msg_cksum = 0;
96 }
97
98 /*
99  * Get a count of the number of unread characters in the message buffer.
100  */
101 int
102 msgbuf_getcount(struct msgbuf *mbp)
103 {
104         u_int len;
105
106         len = MSGBUF_SEQSUB(mbp, mbp->msg_wseq, mbp->msg_rseq);
107         if (len > mbp->msg_size)
108                 len = mbp->msg_size;
109         return (len);
110 }
111
112 /*
113  * Append a character to a message buffer.  This function can be
114  * considered fully reentrant so long as the number of concurrent
115  * callers is less than the number of characters in the buffer.
116  * However, the message buffer is only guaranteed to be consistent
117  * for reading when there are no callers in this function.
118  */
119 void
120 msgbuf_addchar(struct msgbuf *mbp, int c)
121 {
122         u_int new_seq, pos, seq;
123
124         do {
125                 seq = mbp->msg_wseq;
126                 new_seq = MSGBUF_SEQNORM(mbp, seq + 1);
127         } while (atomic_cmpset_rel_int(&mbp->msg_wseq, seq, new_seq) == 0);
128         pos = MSGBUF_SEQ_TO_POS(mbp, seq);
129         atomic_add_int(&mbp->msg_cksum, (u_int)(u_char)c -
130             (u_int)(u_char)mbp->msg_ptr[pos]);
131         mbp->msg_ptr[pos] = c;
132 }
133
134 /*
135  * Read and mark as read a character from a message buffer.
136  * Returns the character, or -1 if no characters are available.
137  */
138 int
139 msgbuf_getchar(struct msgbuf *mbp)
140 {
141         u_int len, wseq;
142         int c;
143
144         wseq = mbp->msg_wseq;
145         len = MSGBUF_SEQSUB(mbp, wseq, mbp->msg_rseq);
146         if (len == 0)
147                 return (-1);
148         if (len > mbp->msg_size)
149                 mbp->msg_rseq = MSGBUF_SEQNORM(mbp, wseq - mbp->msg_size);
150         c = (u_char)mbp->msg_ptr[MSGBUF_SEQ_TO_POS(mbp, mbp->msg_rseq)];
151         mbp->msg_rseq = MSGBUF_SEQNORM(mbp, mbp->msg_rseq + 1);
152         return (c);
153 }
154
155 /*
156  * Read and mark as read a number of characters from a message buffer.
157  * Returns the number of characters that were placed in `buf'.
158  */
159 int
160 msgbuf_getbytes(struct msgbuf *mbp, char *buf, int buflen)
161 {
162         u_int len, pos, wseq;
163
164         wseq = mbp->msg_wseq;
165         len = MSGBUF_SEQSUB(mbp, wseq, mbp->msg_rseq);
166         if (len == 0)
167                 return (0);
168         if (len > mbp->msg_size) {
169                 mbp->msg_rseq = MSGBUF_SEQNORM(mbp, wseq - mbp->msg_size);
170                 len = mbp->msg_size;
171         }
172         pos = MSGBUF_SEQ_TO_POS(mbp, mbp->msg_rseq);
173         len = min(len, mbp->msg_size - pos);
174         len = min(len, (u_int)buflen);
175
176         bcopy(&mbp->msg_ptr[pos], buf, len);
177         mbp->msg_rseq = MSGBUF_SEQNORM(mbp, mbp->msg_rseq + len);
178         return (len);
179 }
180
181 /*
182  * Peek at the full contents of a message buffer without marking any
183  * data as read. `seqp' should point to an unsigned integer that
184  * msgbuf_peekbytes() can use to retain state between calls so that
185  * the whole message buffer can be read in multiple short reads.
186  * To initialise this variable to the start of the message buffer,
187  * call msgbuf_peekbytes() with a NULL `buf' parameter.
188  *
189  * Returns the number of characters that were placed in `buf'.
190  */
191 int
192 msgbuf_peekbytes(struct msgbuf *mbp, char *buf, int buflen, u_int *seqp)
193 {
194         u_int len, pos, wseq;
195
196         if (buf == NULL) {
197                 /* Just initialise *seqp. */
198                 *seqp = MSGBUF_SEQNORM(mbp, mbp->msg_wseq - mbp->msg_size);
199                 return (0);
200         }
201
202         wseq = mbp->msg_wseq;
203         len = MSGBUF_SEQSUB(mbp, wseq, *seqp);
204         if (len == 0)
205                 return (0);
206         if (len > mbp->msg_size) {
207                 *seqp = MSGBUF_SEQNORM(mbp, wseq - mbp->msg_size);
208                 len = mbp->msg_size;
209         }
210         pos = MSGBUF_SEQ_TO_POS(mbp, *seqp);
211         len = min(len, mbp->msg_size - pos);
212         len = min(len, (u_int)buflen);
213         bcopy(&mbp->msg_ptr[MSGBUF_SEQ_TO_POS(mbp, *seqp)], buf, len);
214         *seqp = MSGBUF_SEQNORM(mbp, *seqp + len);
215         return (len);
216 }
217
218 /*
219  * Compute the checksum for the complete message buffer contents.
220  */
221 static u_int
222 msgbuf_cksum(struct msgbuf *mbp)
223 {
224         u_int i, sum;
225
226         sum = 0;
227         for (i = 0; i < mbp->msg_size; i++)
228                 sum += (u_char)mbp->msg_ptr[i];
229         return (sum);
230 }
231
232 /*
233  * Copy from one message buffer to another.
234  */
235 void
236 msgbuf_copy(struct msgbuf *src, struct msgbuf *dst)
237 {
238         int c;
239
240         while ((c = msgbuf_getchar(src)) >= 0)
241                 msgbuf_addchar(dst, c);
242 }