subr_msgbuf.c revision 231814
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: head/sys/kern/subr_msgbuf.c 231814 2012-02-16 05:11:35Z eadler $ 26 */ 27 28/* 29 * Generic message buffer support routines. 30 */ 31 32#include <sys/param.h> 33#include <sys/systm.h> 34#include <sys/lock.h> 35#include <sys/kernel.h> 36#include <sys/mutex.h> 37#include <sys/msgbuf.h> 38#include <sys/sysctl.h> 39 40/* 41 * Maximum number conversion buffer length: uintmax_t in base 2, plus <> 42 * around the priority, and a terminating NUL. 43 */ 44#define MAXPRIBUF (sizeof(intmax_t) * NBBY + 3) 45 46/* Read/write sequence numbers are modulo a multiple of the buffer size. */ 47#define SEQMOD(size) ((size) * 16) 48 49static u_int msgbuf_cksum(struct msgbuf *mbp); 50 51/* 52 * 53 */ 54static int msgbuf_show_timestamp = 0; 55SYSCTL_INT(_kern, OID_AUTO, msgbuf_show_timestamp, CTLFLAG_RW | CTLFLAG_TUN, 56 &msgbuf_show_timestamp, 0, "Show timestamp in msgbuf"); 57TUNABLE_INT("kern.msgbuf_show_timestamp", &msgbuf_show_timestamp); 58 59/* 60 * Initialize a message buffer of the specified size at the specified 61 * location. This also zeros the buffer area. 62 */ 63void 64msgbuf_init(struct msgbuf *mbp, void *ptr, int size) 65{ 66 67 mbp->msg_ptr = ptr; 68 mbp->msg_size = size; 69 mbp->msg_seqmod = SEQMOD(size); 70 msgbuf_clear(mbp); 71 mbp->msg_magic = MSG_MAGIC; 72 mbp->msg_lastpri = -1; 73 mbp->msg_flags = 0; 74 bzero(&mbp->msg_lock, sizeof(mbp->msg_lock)); 75 mtx_init(&mbp->msg_lock, "msgbuf", NULL, MTX_SPIN); 76} 77 78/* 79 * Reinitialize a message buffer, retaining its previous contents if 80 * the size and checksum are correct. If the old contents cannot be 81 * recovered, the message buffer is cleared. 82 */ 83void 84msgbuf_reinit(struct msgbuf *mbp, void *ptr, int size) 85{ 86 u_int cksum; 87 88 if (mbp->msg_magic != MSG_MAGIC || mbp->msg_size != size) { 89 msgbuf_init(mbp, ptr, size); 90 return; 91 } 92 mbp->msg_seqmod = SEQMOD(size); 93 mbp->msg_wseq = MSGBUF_SEQNORM(mbp, mbp->msg_wseq); 94 mbp->msg_rseq = MSGBUF_SEQNORM(mbp, mbp->msg_rseq); 95 mbp->msg_ptr = ptr; 96 cksum = msgbuf_cksum(mbp); 97 if (cksum != mbp->msg_cksum) { 98 if (bootverbose) { 99 printf("msgbuf cksum mismatch (read %x, calc %x)\n", 100 mbp->msg_cksum, cksum); 101 printf("Old msgbuf not recovered\n"); 102 } 103 msgbuf_clear(mbp); 104 } 105 106 mbp->msg_lastpri = -1; 107 /* Assume that the old message buffer didn't end in a newline. */ 108 mbp->msg_flags |= MSGBUF_NEEDNL; 109 bzero(&mbp->msg_lock, sizeof(mbp->msg_lock)); 110 mtx_init(&mbp->msg_lock, "msgbuf", NULL, MTX_SPIN); 111} 112 113/* 114 * Clear the message buffer. 115 */ 116void 117msgbuf_clear(struct msgbuf *mbp) 118{ 119 120 bzero(mbp->msg_ptr, mbp->msg_size); 121 mbp->msg_wseq = 0; 122 mbp->msg_rseq = 0; 123 mbp->msg_cksum = 0; 124} 125 126/* 127 * Get a count of the number of unread characters in the message buffer. 128 */ 129int 130msgbuf_getcount(struct msgbuf *mbp) 131{ 132 u_int len; 133 134 len = MSGBUF_SEQSUB(mbp, mbp->msg_wseq, mbp->msg_rseq); 135 if (len > mbp->msg_size) 136 len = mbp->msg_size; 137 return (len); 138} 139 140/* 141 * Add a character into the message buffer, and update the checksum and 142 * sequence number. 143 * 144 * The caller should hold the message buffer spinlock. 145 */ 146static inline void 147__msgbuf_do_addchar(struct msgbuf * const mbp, u_int * const seq, const int c) 148{ 149 u_int pos; 150 151 /* Make sure we properly wrap the sequence number. */ 152 pos = MSGBUF_SEQ_TO_POS(mbp, *seq); 153 154 mbp->msg_cksum += (u_int)c - 155 (u_int)(u_char)mbp->msg_ptr[pos]; 156 157 mbp->msg_ptr[pos] = c; 158 159 *seq = MSGBUF_SEQNORM(mbp, *seq + 1); 160} 161 162static inline void 163msgbuf_do_addchar(struct msgbuf * const mbp, u_int * const seq, const int c) 164{ 165 166 if (msgbuf_show_timestamp && 167 (mbp->msg_flags & MSGBUF_NEXT_NEW_LINE) != 0) { 168 char buf[32]; 169 char const *bufp; 170 struct timespec ts; 171 int err; 172 173 getnanouptime(&ts); 174 err = snprintf(buf, sizeof (buf), "[%jd.%ld] ", 175 (intmax_t)ts.tv_sec, ts.tv_nsec / 1000); 176 177 for (bufp = buf; *bufp != '\0'; bufp++) 178 __msgbuf_do_addchar(mbp, seq, *bufp); 179 180 mbp->msg_flags &= ~MSGBUF_NEXT_NEW_LINE; 181 } 182 183 __msgbuf_do_addchar(mbp, seq, c); 184 185 if (c == '\n') 186 mbp->msg_flags |= MSGBUF_NEXT_NEW_LINE; 187} 188 189/* 190 * Append a character to a message buffer. 191 */ 192void 193msgbuf_addchar(struct msgbuf *mbp, int c) 194{ 195 mtx_lock_spin(&mbp->msg_lock); 196 197 msgbuf_do_addchar(mbp, &mbp->msg_wseq, c); 198 199 mtx_unlock_spin(&mbp->msg_lock); 200} 201 202/* 203 * Append a NUL-terminated string with a priority to a message buffer. 204 * Filter carriage returns if the caller requests it. 205 * 206 * XXX The carriage return filtering behavior is present in the 207 * msglogchar() API, however testing has shown that we don't seem to send 208 * carriage returns down this path. So do we still need it? 209 */ 210void 211msgbuf_addstr(struct msgbuf *mbp, int pri, char *str, int filter_cr) 212{ 213 u_int seq; 214 size_t len, prefix_len; 215 char prefix[MAXPRIBUF]; 216 int nl, i; 217 218 len = strlen(str); 219 prefix_len = 0; 220 nl = 0; 221 222 /* If we have a zero-length string, no need to do anything. */ 223 if (len == 0) 224 return; 225 226 mtx_lock_spin(&mbp->msg_lock); 227 228 /* 229 * If this is true, we may need to insert a new priority sequence, 230 * so prepare the prefix. 231 */ 232 if (pri != -1) 233 prefix_len = sprintf(prefix, "<%d>", pri); 234 235 /* 236 * Starting write sequence number. 237 */ 238 seq = mbp->msg_wseq; 239 240 /* 241 * Whenever there is a change in priority, we have to insert a 242 * newline, and a priority prefix if the priority is not -1. Here 243 * we detect whether there was a priority change, and whether we 244 * did not end with a newline. If that is the case, we need to 245 * insert a newline before this string. 246 */ 247 if (mbp->msg_lastpri != pri && (mbp->msg_flags & MSGBUF_NEEDNL) != 0) { 248 249 msgbuf_do_addchar(mbp, &seq, '\n'); 250 mbp->msg_flags &= ~MSGBUF_NEEDNL; 251 } 252 253 for (i = 0; i < len; i++) { 254 /* 255 * If we just had a newline, and the priority is not -1 256 * (and therefore prefix_len != 0), then we need a priority 257 * prefix for this line. 258 */ 259 if ((mbp->msg_flags & MSGBUF_NEEDNL) == 0 && prefix_len != 0) { 260 int j; 261 262 for (j = 0; j < prefix_len; j++) 263 msgbuf_do_addchar(mbp, &seq, prefix[j]); 264 } 265 266 /* 267 * Don't copy carriage returns if the caller requested 268 * filtering. 269 * 270 * XXX This matches the behavior of msglogchar(), but is it 271 * necessary? Testing has shown that we don't seem to get 272 * carriage returns here. 273 */ 274 if ((filter_cr != 0) && (str[i] == '\r')) 275 continue; 276 277 /* 278 * Clear this flag if we see a newline. This affects whether 279 * we need to insert a new prefix or insert a newline later. 280 */ 281 if (str[i] == '\n') 282 mbp->msg_flags &= ~MSGBUF_NEEDNL; 283 else 284 mbp->msg_flags |= MSGBUF_NEEDNL; 285 286 msgbuf_do_addchar(mbp, &seq, str[i]); 287 } 288 /* 289 * Update the write sequence number for the actual number of 290 * characters we put in the message buffer. (Depends on whether 291 * carriage returns are filtered.) 292 */ 293 mbp->msg_wseq = seq; 294 295 /* 296 * Set the last priority. 297 */ 298 mbp->msg_lastpri = pri; 299 300 mtx_unlock_spin(&mbp->msg_lock); 301 302} 303 304/* 305 * Read and mark as read a character from a message buffer. 306 * Returns the character, or -1 if no characters are available. 307 */ 308int 309msgbuf_getchar(struct msgbuf *mbp) 310{ 311 u_int len, wseq; 312 int c; 313 314 mtx_lock_spin(&mbp->msg_lock); 315 316 wseq = mbp->msg_wseq; 317 len = MSGBUF_SEQSUB(mbp, wseq, mbp->msg_rseq); 318 if (len == 0) { 319 mtx_unlock_spin(&mbp->msg_lock); 320 return (-1); 321 } 322 if (len > mbp->msg_size) 323 mbp->msg_rseq = MSGBUF_SEQNORM(mbp, wseq - mbp->msg_size); 324 c = (u_char)mbp->msg_ptr[MSGBUF_SEQ_TO_POS(mbp, mbp->msg_rseq)]; 325 mbp->msg_rseq = MSGBUF_SEQNORM(mbp, mbp->msg_rseq + 1); 326 327 mtx_unlock_spin(&mbp->msg_lock); 328 329 return (c); 330} 331 332/* 333 * Read and mark as read a number of characters from a message buffer. 334 * Returns the number of characters that were placed in `buf'. 335 */ 336int 337msgbuf_getbytes(struct msgbuf *mbp, char *buf, int buflen) 338{ 339 u_int len, pos, wseq; 340 341 mtx_lock_spin(&mbp->msg_lock); 342 343 wseq = mbp->msg_wseq; 344 len = MSGBUF_SEQSUB(mbp, wseq, mbp->msg_rseq); 345 if (len == 0) { 346 mtx_unlock_spin(&mbp->msg_lock); 347 return (0); 348 } 349 if (len > mbp->msg_size) { 350 mbp->msg_rseq = MSGBUF_SEQNORM(mbp, wseq - mbp->msg_size); 351 len = mbp->msg_size; 352 } 353 pos = MSGBUF_SEQ_TO_POS(mbp, mbp->msg_rseq); 354 len = min(len, mbp->msg_size - pos); 355 len = min(len, (u_int)buflen); 356 357 bcopy(&mbp->msg_ptr[pos], buf, len); 358 mbp->msg_rseq = MSGBUF_SEQNORM(mbp, mbp->msg_rseq + len); 359 360 mtx_unlock_spin(&mbp->msg_lock); 361 362 return (len); 363} 364 365/* 366 * Peek at the full contents of a message buffer without marking any 367 * data as read. `seqp' should point to an unsigned integer that 368 * msgbuf_peekbytes() can use to retain state between calls so that 369 * the whole message buffer can be read in multiple short reads. 370 * To initialise this variable to the start of the message buffer, 371 * call msgbuf_peekbytes() with a NULL `buf' parameter. 372 * 373 * Returns the number of characters that were placed in `buf'. 374 */ 375int 376msgbuf_peekbytes(struct msgbuf *mbp, char *buf, int buflen, u_int *seqp) 377{ 378 u_int len, pos, wseq; 379 380 mtx_lock_spin(&mbp->msg_lock); 381 382 if (buf == NULL) { 383 /* Just initialise *seqp. */ 384 *seqp = MSGBUF_SEQNORM(mbp, mbp->msg_wseq - mbp->msg_size); 385 mtx_unlock_spin(&mbp->msg_lock); 386 return (0); 387 } 388 389 wseq = mbp->msg_wseq; 390 len = MSGBUF_SEQSUB(mbp, wseq, *seqp); 391 if (len == 0) { 392 mtx_unlock_spin(&mbp->msg_lock); 393 return (0); 394 } 395 if (len > mbp->msg_size) { 396 *seqp = MSGBUF_SEQNORM(mbp, wseq - mbp->msg_size); 397 len = mbp->msg_size; 398 } 399 pos = MSGBUF_SEQ_TO_POS(mbp, *seqp); 400 len = min(len, mbp->msg_size - pos); 401 len = min(len, (u_int)buflen); 402 bcopy(&mbp->msg_ptr[MSGBUF_SEQ_TO_POS(mbp, *seqp)], buf, len); 403 *seqp = MSGBUF_SEQNORM(mbp, *seqp + len); 404 405 mtx_unlock_spin(&mbp->msg_lock); 406 407 return (len); 408} 409 410/* 411 * Compute the checksum for the complete message buffer contents. 412 */ 413static u_int 414msgbuf_cksum(struct msgbuf *mbp) 415{ 416 u_int i, sum; 417 418 sum = 0; 419 for (i = 0; i < mbp->msg_size; i++) 420 sum += (u_char)mbp->msg_ptr[i]; 421 return (sum); 422} 423 424/* 425 * Copy from one message buffer to another. 426 */ 427void 428msgbuf_copy(struct msgbuf *src, struct msgbuf *dst) 429{ 430 int c; 431 432 while ((c = msgbuf_getchar(src)) >= 0) 433 msgbuf_addchar(dst, c); 434} 435