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