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