1 1.76 kamil /* $NetBSD: sysv_msg.c,v 1.76 2019/10/04 23:20:22 kamil Exp $ */ 2 1.26 thorpej 3 1.26 thorpej /*- 4 1.48 ad * Copyright (c) 1999, 2006, 2007 The NetBSD Foundation, Inc. 5 1.26 thorpej * All rights reserved. 6 1.26 thorpej * 7 1.26 thorpej * This code is derived from software contributed to The NetBSD Foundation 8 1.26 thorpej * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, 9 1.48 ad * NASA Ames Research Center, and by Andrew Doran. 10 1.26 thorpej * 11 1.26 thorpej * Redistribution and use in source and binary forms, with or without 12 1.26 thorpej * modification, are permitted provided that the following conditions 13 1.26 thorpej * are met: 14 1.26 thorpej * 1. Redistributions of source code must retain the above copyright 15 1.26 thorpej * notice, this list of conditions and the following disclaimer. 16 1.26 thorpej * 2. Redistributions in binary form must reproduce the above copyright 17 1.26 thorpej * notice, this list of conditions and the following disclaimer in the 18 1.26 thorpej * documentation and/or other materials provided with the distribution. 19 1.26 thorpej * 20 1.26 thorpej * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 1.26 thorpej * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 1.26 thorpej * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 1.26 thorpej * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 1.26 thorpej * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 1.26 thorpej * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 1.26 thorpej * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 1.26 thorpej * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 1.26 thorpej * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 1.26 thorpej * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 1.26 thorpej * POSSIBILITY OF SUCH DAMAGE. 31 1.26 thorpej */ 32 1.9 cgd 33 1.1 cgd /* 34 1.1 cgd * Implementation of SVID messages 35 1.1 cgd * 36 1.26 thorpej * Author: Daniel Boulet 37 1.1 cgd * 38 1.1 cgd * Copyright 1993 Daniel Boulet and RTMX Inc. 39 1.1 cgd * 40 1.1 cgd * This system call was implemented by Daniel Boulet under contract from RTMX. 41 1.1 cgd * 42 1.1 cgd * Redistribution and use in source forms, with and without modification, 43 1.1 cgd * are permitted provided that this entire comment appears intact. 44 1.1 cgd * 45 1.1 cgd * Redistribution in binary form may occur without any restrictions. 46 1.1 cgd * Obviously, it would be nice if you gave credit where credit is due 47 1.1 cgd * but requiring it would be too onerous. 48 1.1 cgd * 49 1.1 cgd * This software is provided ``AS IS'' without any warranties of any kind. 50 1.1 cgd */ 51 1.33 lukem 52 1.33 lukem #include <sys/cdefs.h> 53 1.76 kamil __KERNEL_RCSID(0, "$NetBSD: sysv_msg.c,v 1.76 2019/10/04 23:20:22 kamil Exp $"); 54 1.23 tron 55 1.67 pgoyette #ifdef _KERNEL_OPT 56 1.67 pgoyette #include "opt_sysv.h" 57 1.67 pgoyette #endif 58 1.1 cgd 59 1.2 mycroft #include <sys/param.h> 60 1.2 mycroft #include <sys/kernel.h> 61 1.2 mycroft #include <sys/msg.h> 62 1.29 simonb #include <sys/sysctl.h> 63 1.29 simonb #include <sys/mount.h> /* XXX for <sys/syscallargs.h> */ 64 1.10 cgd #include <sys/syscallargs.h> 65 1.42 elad #include <sys/kauth.h> 66 1.18 christos 67 1.1 cgd #define MSG_DEBUG 68 1.1 cgd #undef MSG_DEBUG_OK 69 1.1 cgd 70 1.20 christos #ifdef MSG_DEBUG_OK 71 1.21 christos #define MSG_PRINTF(a) printf a 72 1.20 christos #else 73 1.20 christos #define MSG_PRINTF(a) 74 1.20 christos #endif 75 1.20 christos 76 1.36 jdolecek static int nfree_msgmaps; /* # of free map entries */ 77 1.36 jdolecek static short free_msgmaps; /* head of linked list of free map entries */ 78 1.36 jdolecek static struct __msg *free_msghdrs; /* list of free msg headers */ 79 1.36 jdolecek static char *msgpool; /* MSGMAX byte long msg buffer pool */ 80 1.36 jdolecek static struct msgmap *msgmaps; /* MSGSEG msgmap structures */ 81 1.36 jdolecek static struct __msg *msghdrs; /* MSGTQL msg headers */ 82 1.48 ad 83 1.48 ad kmsq_t *msqs; /* MSGMNI msqid_ds struct's */ 84 1.48 ad kmutex_t msgmutex; /* subsystem lock */ 85 1.1 cgd 86 1.51 rmind static u_int msg_waiters = 0; /* total number of msgrcv waiters */ 87 1.51 rmind static bool msg_realloc_state; 88 1.51 rmind static kcondvar_t msg_realloc_cv; 89 1.51 rmind 90 1.37 junyoung static void msg_freehdr(struct __msg *); 91 1.18 christos 92 1.68 pgoyette extern int kern_has_sysvmsg; 93 1.68 pgoyette 94 1.70 pgoyette SYSCTL_SETUP_PROTO(sysctl_ipc_msg_setup); 95 1.70 pgoyette 96 1.74 pgoyette int 97 1.75 pgoyette msginit(void) 98 1.1 cgd { 99 1.36 jdolecek int i, sz; 100 1.36 jdolecek vaddr_t v; 101 1.1 cgd 102 1.3 mycroft /* 103 1.3 mycroft * msginfo.msgssz should be a power of two for efficiency reasons. 104 1.3 mycroft * It is also pretty silly if msginfo.msgssz is less than 8 105 1.3 mycroft * or greater than about 256 so ... 106 1.3 mycroft */ 107 1.1 cgd 108 1.3 mycroft i = 8; 109 1.3 mycroft while (i < 1024 && i != msginfo.msgssz) 110 1.3 mycroft i <<= 1; 111 1.50 rmind if (i != msginfo.msgssz) { 112 1.74 pgoyette printf("msginfo.msgssz = %d, not a small power of 2", 113 1.50 rmind msginfo.msgssz); 114 1.74 pgoyette return EINVAL; 115 1.3 mycroft } 116 1.3 mycroft 117 1.3 mycroft if (msginfo.msgseg > 32767) { 118 1.74 pgoyette printf("msginfo.msgseg = %d > 32767", msginfo.msgseg); 119 1.74 pgoyette return EINVAL; 120 1.3 mycroft } 121 1.3 mycroft 122 1.51 rmind /* Allocate the wired memory for our structures */ 123 1.51 rmind sz = ALIGN(msginfo.msgmax) + 124 1.51 rmind ALIGN(msginfo.msgseg * sizeof(struct msgmap)) + 125 1.51 rmind ALIGN(msginfo.msgtql * sizeof(struct __msg)) + 126 1.51 rmind ALIGN(msginfo.msgmni * sizeof(kmsq_t)); 127 1.62 uebayasi sz = round_page(sz); 128 1.62 uebayasi v = uvm_km_alloc(kernel_map, sz, 0, UVM_KMF_WIRED|UVM_KMF_ZERO); 129 1.74 pgoyette if (v == 0) { 130 1.74 pgoyette printf("sysv_msg: cannot allocate memory"); 131 1.74 pgoyette return ENOMEM; 132 1.74 pgoyette } 133 1.36 jdolecek msgpool = (void *)v; 134 1.58 rmind msgmaps = (void *)((uintptr_t)msgpool + ALIGN(msginfo.msgmax)); 135 1.58 rmind msghdrs = (void *)((uintptr_t)msgmaps + 136 1.58 rmind ALIGN(msginfo.msgseg * sizeof(struct msgmap))); 137 1.58 rmind msqs = (void *)((uintptr_t)msghdrs + 138 1.58 rmind ALIGN(msginfo.msgtql * sizeof(struct __msg))); 139 1.50 rmind 140 1.50 rmind for (i = 0; i < (msginfo.msgseg - 1); i++) 141 1.50 rmind msgmaps[i].next = i + 1; 142 1.50 rmind msgmaps[msginfo.msgseg - 1].next = -1; 143 1.50 rmind 144 1.3 mycroft free_msgmaps = 0; 145 1.3 mycroft nfree_msgmaps = msginfo.msgseg; 146 1.3 mycroft 147 1.50 rmind for (i = 0; i < (msginfo.msgtql - 1); i++) { 148 1.3 mycroft msghdrs[i].msg_type = 0; 149 1.50 rmind msghdrs[i].msg_next = &msghdrs[i + 1]; 150 1.50 rmind } 151 1.50 rmind i = msginfo.msgtql - 1; 152 1.50 rmind msghdrs[i].msg_type = 0; 153 1.50 rmind msghdrs[i].msg_next = NULL; 154 1.3 mycroft free_msghdrs = &msghdrs[0]; 155 1.3 mycroft 156 1.4 mycroft for (i = 0; i < msginfo.msgmni; i++) { 157 1.48 ad cv_init(&msqs[i].msq_cv, "msgwait"); 158 1.50 rmind /* Implies entry is available */ 159 1.50 rmind msqs[i].msq_u.msg_qbytes = 0; 160 1.50 rmind /* Reset to a known value */ 161 1.50 rmind msqs[i].msq_u.msg_perm._seq = 0; 162 1.3 mycroft } 163 1.48 ad 164 1.48 ad mutex_init(&msgmutex, MUTEX_DEFAULT, IPL_NONE); 165 1.51 rmind cv_init(&msg_realloc_cv, "msgrealc"); 166 1.51 rmind msg_realloc_state = false; 167 1.63 elad 168 1.68 pgoyette kern_has_sysvmsg = 1; 169 1.68 pgoyette 170 1.74 pgoyette return 0; 171 1.51 rmind } 172 1.51 rmind 173 1.69 pgoyette int 174 1.69 pgoyette msgfini(void) 175 1.69 pgoyette { 176 1.69 pgoyette int i, sz; 177 1.69 pgoyette vaddr_t v = (vaddr_t)msgpool; 178 1.69 pgoyette 179 1.69 pgoyette mutex_enter(&msgmutex); 180 1.69 pgoyette for (i = 0; i < msginfo.msgmni; i++) { 181 1.69 pgoyette if (msqs[i].msq_u.msg_qbytes != 0) { 182 1.69 pgoyette mutex_exit(&msgmutex); 183 1.69 pgoyette return 1; /* queue not available, prevent unload! */ 184 1.69 pgoyette } 185 1.69 pgoyette } 186 1.69 pgoyette /* 187 1.69 pgoyette * Destroy all condvars and free the memory we're using 188 1.69 pgoyette */ 189 1.69 pgoyette for (i = 0; i < msginfo.msgmni; i++) { 190 1.69 pgoyette cv_destroy(&msqs[i].msq_cv); 191 1.69 pgoyette } 192 1.69 pgoyette sz = ALIGN(msginfo.msgmax) + 193 1.69 pgoyette ALIGN(msginfo.msgseg * sizeof(struct msgmap)) + 194 1.69 pgoyette ALIGN(msginfo.msgtql * sizeof(struct __msg)) + 195 1.69 pgoyette ALIGN(msginfo.msgmni * sizeof(kmsq_t)); 196 1.69 pgoyette sz = round_page(sz); 197 1.69 pgoyette uvm_km_free(kernel_map, v, sz, UVM_KMF_WIRED); 198 1.69 pgoyette 199 1.71 christos cv_destroy(&msg_realloc_cv); 200 1.69 pgoyette mutex_exit(&msgmutex); 201 1.69 pgoyette mutex_destroy(&msgmutex); 202 1.69 pgoyette 203 1.69 pgoyette kern_has_sysvmsg = 0; 204 1.69 pgoyette 205 1.69 pgoyette return 0; 206 1.69 pgoyette } 207 1.69 pgoyette 208 1.51 rmind static int 209 1.51 rmind msgrealloc(int newmsgmni, int newmsgseg) 210 1.51 rmind { 211 1.51 rmind struct msgmap *new_msgmaps; 212 1.51 rmind struct __msg *new_msghdrs, *new_free_msghdrs; 213 1.51 rmind char *old_msgpool, *new_msgpool; 214 1.51 rmind kmsq_t *new_msqs; 215 1.51 rmind vaddr_t v; 216 1.51 rmind int i, sz, msqid, newmsgmax, new_nfree_msgmaps; 217 1.51 rmind short new_free_msgmaps; 218 1.51 rmind 219 1.51 rmind if (newmsgmni < 1 || newmsgseg < 1) 220 1.51 rmind return EINVAL; 221 1.51 rmind 222 1.51 rmind /* Allocate the wired memory for our structures */ 223 1.51 rmind newmsgmax = msginfo.msgssz * newmsgseg; 224 1.51 rmind sz = ALIGN(newmsgmax) + 225 1.51 rmind ALIGN(newmsgseg * sizeof(struct msgmap)) + 226 1.51 rmind ALIGN(msginfo.msgtql * sizeof(struct __msg)) + 227 1.51 rmind ALIGN(newmsgmni * sizeof(kmsq_t)); 228 1.62 uebayasi sz = round_page(sz); 229 1.62 uebayasi v = uvm_km_alloc(kernel_map, sz, 0, UVM_KMF_WIRED|UVM_KMF_ZERO); 230 1.51 rmind if (v == 0) 231 1.51 rmind return ENOMEM; 232 1.51 rmind 233 1.51 rmind mutex_enter(&msgmutex); 234 1.51 rmind if (msg_realloc_state) { 235 1.51 rmind mutex_exit(&msgmutex); 236 1.51 rmind uvm_km_free(kernel_map, v, sz, UVM_KMF_WIRED); 237 1.51 rmind return EBUSY; 238 1.51 rmind } 239 1.51 rmind msg_realloc_state = true; 240 1.51 rmind if (msg_waiters) { 241 1.51 rmind /* 242 1.51 rmind * Mark reallocation state, wake-up all waiters, 243 1.51 rmind * and wait while they will all exit. 244 1.51 rmind */ 245 1.51 rmind for (i = 0; i < msginfo.msgmni; i++) 246 1.51 rmind cv_broadcast(&msqs[i].msq_cv); 247 1.51 rmind while (msg_waiters) 248 1.51 rmind cv_wait(&msg_realloc_cv, &msgmutex); 249 1.51 rmind } 250 1.51 rmind old_msgpool = msgpool; 251 1.51 rmind 252 1.51 rmind /* We cannot reallocate less memory than we use */ 253 1.51 rmind i = 0; 254 1.51 rmind for (msqid = 0; msqid < msginfo.msgmni; msqid++) { 255 1.51 rmind struct msqid_ds *mptr; 256 1.51 rmind kmsq_t *msq; 257 1.51 rmind 258 1.51 rmind msq = &msqs[msqid]; 259 1.51 rmind mptr = &msq->msq_u; 260 1.51 rmind if (mptr->msg_qbytes || (mptr->msg_perm.mode & MSG_LOCKED)) 261 1.51 rmind i = msqid; 262 1.51 rmind } 263 1.51 rmind if (i >= newmsgmni || (msginfo.msgseg - nfree_msgmaps) > newmsgseg) { 264 1.51 rmind mutex_exit(&msgmutex); 265 1.51 rmind uvm_km_free(kernel_map, v, sz, UVM_KMF_WIRED); 266 1.51 rmind return EBUSY; 267 1.51 rmind } 268 1.51 rmind 269 1.51 rmind new_msgpool = (void *)v; 270 1.58 rmind new_msgmaps = (void *)((uintptr_t)new_msgpool + ALIGN(newmsgmax)); 271 1.58 rmind new_msghdrs = (void *)((uintptr_t)new_msgmaps + 272 1.58 rmind ALIGN(newmsgseg * sizeof(struct msgmap))); 273 1.58 rmind new_msqs = (void *)((uintptr_t)new_msghdrs + 274 1.58 rmind ALIGN(msginfo.msgtql * sizeof(struct __msg))); 275 1.51 rmind 276 1.51 rmind /* Initialize the structures */ 277 1.51 rmind for (i = 0; i < (newmsgseg - 1); i++) 278 1.51 rmind new_msgmaps[i].next = i + 1; 279 1.51 rmind new_msgmaps[newmsgseg - 1].next = -1; 280 1.51 rmind new_free_msgmaps = 0; 281 1.51 rmind new_nfree_msgmaps = newmsgseg; 282 1.51 rmind 283 1.51 rmind for (i = 0; i < (msginfo.msgtql - 1); i++) { 284 1.51 rmind new_msghdrs[i].msg_type = 0; 285 1.51 rmind new_msghdrs[i].msg_next = &new_msghdrs[i + 1]; 286 1.51 rmind } 287 1.51 rmind i = msginfo.msgtql - 1; 288 1.51 rmind new_msghdrs[i].msg_type = 0; 289 1.51 rmind new_msghdrs[i].msg_next = NULL; 290 1.51 rmind new_free_msghdrs = &new_msghdrs[0]; 291 1.51 rmind 292 1.51 rmind for (i = 0; i < newmsgmni; i++) { 293 1.51 rmind new_msqs[i].msq_u.msg_qbytes = 0; 294 1.51 rmind new_msqs[i].msq_u.msg_perm._seq = 0; 295 1.51 rmind cv_init(&new_msqs[i].msq_cv, "msgwait"); 296 1.51 rmind } 297 1.51 rmind 298 1.51 rmind /* 299 1.65 msaitoh * Copy all message queue identifiers, message headers and buffer 300 1.51 rmind * pools to the new memory location. 301 1.51 rmind */ 302 1.51 rmind for (msqid = 0; msqid < msginfo.msgmni; msqid++) { 303 1.51 rmind struct __msg *nmsghdr, *msghdr, *pmsghdr; 304 1.51 rmind struct msqid_ds *nmptr, *mptr; 305 1.51 rmind kmsq_t *nmsq, *msq; 306 1.51 rmind 307 1.51 rmind msq = &msqs[msqid]; 308 1.51 rmind mptr = &msq->msq_u; 309 1.51 rmind 310 1.51 rmind if (mptr->msg_qbytes == 0 && 311 1.51 rmind (mptr->msg_perm.mode & MSG_LOCKED) == 0) 312 1.51 rmind continue; 313 1.51 rmind 314 1.51 rmind nmsq = &new_msqs[msqid]; 315 1.51 rmind nmptr = &nmsq->msq_u; 316 1.51 rmind memcpy(nmptr, mptr, sizeof(struct msqid_ds)); 317 1.51 rmind 318 1.51 rmind /* 319 1.72 maya * Go through the message headers, and copy each one 320 1.72 maya * by taking the new ones, and thus defragmenting. 321 1.51 rmind */ 322 1.51 rmind nmsghdr = pmsghdr = NULL; 323 1.51 rmind msghdr = mptr->_msg_first; 324 1.51 rmind while (msghdr) { 325 1.51 rmind short nnext = 0, next; 326 1.51 rmind u_short msgsz, segcnt; 327 1.51 rmind 328 1.51 rmind /* Take an entry from the new list of free msghdrs */ 329 1.51 rmind nmsghdr = new_free_msghdrs; 330 1.51 rmind KASSERT(nmsghdr != NULL); 331 1.51 rmind new_free_msghdrs = nmsghdr->msg_next; 332 1.51 rmind 333 1.51 rmind nmsghdr->msg_next = NULL; 334 1.51 rmind if (pmsghdr) { 335 1.51 rmind pmsghdr->msg_next = nmsghdr; 336 1.51 rmind } else { 337 1.51 rmind nmptr->_msg_first = nmsghdr; 338 1.51 rmind pmsghdr = nmsghdr; 339 1.51 rmind } 340 1.51 rmind nmsghdr->msg_ts = msghdr->msg_ts; 341 1.51 rmind nmsghdr->msg_spot = -1; 342 1.51 rmind 343 1.51 rmind /* Compute the amount of segments and reserve them */ 344 1.51 rmind msgsz = msghdr->msg_ts; 345 1.51 rmind segcnt = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz; 346 1.51 rmind if (segcnt == 0) 347 1.51 rmind continue; 348 1.51 rmind while (segcnt--) { 349 1.51 rmind nnext = new_free_msgmaps; 350 1.51 rmind new_free_msgmaps = new_msgmaps[nnext].next; 351 1.51 rmind new_nfree_msgmaps--; 352 1.51 rmind new_msgmaps[nnext].next = nmsghdr->msg_spot; 353 1.51 rmind nmsghdr->msg_spot = nnext; 354 1.51 rmind } 355 1.51 rmind 356 1.51 rmind /* Copy all segments */ 357 1.51 rmind KASSERT(nnext == nmsghdr->msg_spot); 358 1.51 rmind next = msghdr->msg_spot; 359 1.51 rmind while (msgsz > 0) { 360 1.51 rmind size_t tlen; 361 1.51 rmind 362 1.51 rmind if (msgsz >= msginfo.msgssz) { 363 1.51 rmind tlen = msginfo.msgssz; 364 1.51 rmind msgsz -= msginfo.msgssz; 365 1.51 rmind } else { 366 1.51 rmind tlen = msgsz; 367 1.51 rmind msgsz = 0; 368 1.51 rmind } 369 1.51 rmind 370 1.51 rmind /* Copy the message buffer */ 371 1.51 rmind memcpy(&new_msgpool[nnext * msginfo.msgssz], 372 1.51 rmind &msgpool[next * msginfo.msgssz], tlen); 373 1.51 rmind 374 1.51 rmind /* Next entry of the map */ 375 1.51 rmind nnext = msgmaps[nnext].next; 376 1.51 rmind next = msgmaps[next].next; 377 1.51 rmind } 378 1.51 rmind 379 1.51 rmind /* Next message header */ 380 1.51 rmind msghdr = msghdr->msg_next; 381 1.51 rmind } 382 1.51 rmind nmptr->_msg_last = nmsghdr; 383 1.51 rmind } 384 1.51 rmind KASSERT((msginfo.msgseg - nfree_msgmaps) == 385 1.51 rmind (newmsgseg - new_nfree_msgmaps)); 386 1.51 rmind 387 1.51 rmind sz = ALIGN(msginfo.msgmax) + 388 1.51 rmind ALIGN(msginfo.msgseg * sizeof(struct msgmap)) + 389 1.51 rmind ALIGN(msginfo.msgtql * sizeof(struct __msg)) + 390 1.51 rmind ALIGN(msginfo.msgmni * sizeof(kmsq_t)); 391 1.62 uebayasi sz = round_page(sz); 392 1.51 rmind 393 1.51 rmind for (i = 0; i < msginfo.msgmni; i++) 394 1.51 rmind cv_destroy(&msqs[i].msq_cv); 395 1.51 rmind 396 1.51 rmind /* Set the pointers and update the new values */ 397 1.51 rmind msgpool = new_msgpool; 398 1.51 rmind msgmaps = new_msgmaps; 399 1.51 rmind msghdrs = new_msghdrs; 400 1.51 rmind msqs = new_msqs; 401 1.51 rmind 402 1.51 rmind free_msghdrs = new_free_msghdrs; 403 1.51 rmind free_msgmaps = new_free_msgmaps; 404 1.51 rmind nfree_msgmaps = new_nfree_msgmaps; 405 1.51 rmind msginfo.msgmni = newmsgmni; 406 1.51 rmind msginfo.msgseg = newmsgseg; 407 1.51 rmind msginfo.msgmax = newmsgmax; 408 1.51 rmind 409 1.51 rmind /* Reallocation completed - notify all waiters, if any */ 410 1.51 rmind msg_realloc_state = false; 411 1.51 rmind cv_broadcast(&msg_realloc_cv); 412 1.51 rmind mutex_exit(&msgmutex); 413 1.51 rmind 414 1.51 rmind uvm_km_free(kernel_map, (vaddr_t)old_msgpool, sz, UVM_KMF_WIRED); 415 1.51 rmind return 0; 416 1.1 cgd } 417 1.1 cgd 418 1.3 mycroft static void 419 1.40 thorpej msg_freehdr(struct __msg *msghdr) 420 1.1 cgd { 421 1.48 ad 422 1.48 ad KASSERT(mutex_owned(&msgmutex)); 423 1.48 ad 424 1.3 mycroft while (msghdr->msg_ts > 0) { 425 1.3 mycroft short next; 426 1.50 rmind KASSERT(msghdr->msg_spot >= 0); 427 1.50 rmind KASSERT(msghdr->msg_spot < msginfo.msgseg); 428 1.50 rmind 429 1.3 mycroft next = msgmaps[msghdr->msg_spot].next; 430 1.3 mycroft msgmaps[msghdr->msg_spot].next = free_msgmaps; 431 1.3 mycroft free_msgmaps = msghdr->msg_spot; 432 1.5 mycroft nfree_msgmaps++; 433 1.3 mycroft msghdr->msg_spot = next; 434 1.3 mycroft if (msghdr->msg_ts >= msginfo.msgssz) 435 1.3 mycroft msghdr->msg_ts -= msginfo.msgssz; 436 1.3 mycroft else 437 1.3 mycroft msghdr->msg_ts = 0; 438 1.3 mycroft } 439 1.50 rmind KASSERT(msghdr->msg_spot == -1); 440 1.3 mycroft msghdr->msg_next = free_msghdrs; 441 1.3 mycroft free_msghdrs = msghdr; 442 1.1 cgd } 443 1.1 cgd 444 1.1 cgd int 445 1.59 christos sys___msgctl50(struct lwp *l, const struct sys___msgctl50_args *uap, 446 1.59 christos register_t *retval) 447 1.16 thorpej { 448 1.54 dsl /* { 449 1.10 cgd syscallarg(int) msqid; 450 1.10 cgd syscallarg(int) cmd; 451 1.10 cgd syscallarg(struct msqid_ds *) buf; 452 1.54 dsl } */ 453 1.26 thorpej struct msqid_ds msqbuf; 454 1.26 thorpej int cmd, error; 455 1.26 thorpej 456 1.26 thorpej cmd = SCARG(uap, cmd); 457 1.26 thorpej 458 1.26 thorpej if (cmd == IPC_SET) { 459 1.26 thorpej error = copyin(SCARG(uap, buf), &msqbuf, sizeof(msqbuf)); 460 1.26 thorpej if (error) 461 1.26 thorpej return (error); 462 1.26 thorpej } 463 1.26 thorpej 464 1.44 ad error = msgctl1(l, SCARG(uap, msqid), cmd, 465 1.26 thorpej (cmd == IPC_SET || cmd == IPC_STAT) ? &msqbuf : NULL); 466 1.26 thorpej 467 1.26 thorpej if (error == 0 && cmd == IPC_STAT) 468 1.26 thorpej error = copyout(&msqbuf, SCARG(uap, buf), sizeof(msqbuf)); 469 1.26 thorpej 470 1.26 thorpej return (error); 471 1.26 thorpej } 472 1.26 thorpej 473 1.26 thorpej int 474 1.44 ad msgctl1(struct lwp *l, int msqid, int cmd, struct msqid_ds *msqbuf) 475 1.26 thorpej { 476 1.44 ad kauth_cred_t cred = l->l_cred; 477 1.26 thorpej struct msqid_ds *msqptr; 478 1.48 ad kmsq_t *msq; 479 1.26 thorpej int error = 0, ix; 480 1.1 cgd 481 1.26 thorpej MSG_PRINTF(("call to msgctl1(%d, %d)\n", msqid, cmd)); 482 1.1 cgd 483 1.26 thorpej ix = IPCID_TO_IX(msqid); 484 1.1 cgd 485 1.48 ad mutex_enter(&msgmutex); 486 1.48 ad 487 1.26 thorpej if (ix < 0 || ix >= msginfo.msgmni) { 488 1.26 thorpej MSG_PRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", ix, 489 1.20 christos msginfo.msgmni)); 490 1.48 ad error = EINVAL; 491 1.48 ad goto unlock; 492 1.3 mycroft } 493 1.1 cgd 494 1.48 ad msq = &msqs[ix]; 495 1.48 ad msqptr = &msq->msq_u; 496 1.1 cgd 497 1.3 mycroft if (msqptr->msg_qbytes == 0) { 498 1.20 christos MSG_PRINTF(("no such msqid\n")); 499 1.48 ad error = EINVAL; 500 1.48 ad goto unlock; 501 1.3 mycroft } 502 1.26 thorpej if (msqptr->msg_perm._seq != IPCID_TO_SEQ(msqid)) { 503 1.20 christos MSG_PRINTF(("wrong sequence number\n")); 504 1.48 ad error = EINVAL; 505 1.48 ad goto unlock; 506 1.3 mycroft } 507 1.1 cgd 508 1.3 mycroft switch (cmd) { 509 1.3 mycroft case IPC_RMID: 510 1.1 cgd { 511 1.26 thorpej struct __msg *msghdr; 512 1.26 thorpej if ((error = ipcperm(cred, &msqptr->msg_perm, IPC_M)) != 0) 513 1.48 ad break; 514 1.3 mycroft /* Free the message headers */ 515 1.26 thorpej msghdr = msqptr->_msg_first; 516 1.3 mycroft while (msghdr != NULL) { 517 1.26 thorpej struct __msg *msghdr_tmp; 518 1.3 mycroft 519 1.3 mycroft /* Free the segments of each message */ 520 1.26 thorpej msqptr->_msg_cbytes -= msghdr->msg_ts; 521 1.5 mycroft msqptr->msg_qnum--; 522 1.3 mycroft msghdr_tmp = msghdr; 523 1.3 mycroft msghdr = msghdr->msg_next; 524 1.3 mycroft msg_freehdr(msghdr_tmp); 525 1.3 mycroft } 526 1.50 rmind KASSERT(msqptr->_msg_cbytes == 0); 527 1.50 rmind KASSERT(msqptr->msg_qnum == 0); 528 1.1 cgd 529 1.50 rmind /* Mark it as free */ 530 1.50 rmind msqptr->msg_qbytes = 0; 531 1.48 ad cv_broadcast(&msq->msq_cv); 532 1.1 cgd } 533 1.3 mycroft break; 534 1.1 cgd 535 1.3 mycroft case IPC_SET: 536 1.26 thorpej if ((error = ipcperm(cred, &msqptr->msg_perm, IPC_M))) 537 1.48 ad break; 538 1.44 ad if (msqbuf->msg_qbytes > msqptr->msg_qbytes && 539 1.63 elad kauth_authorize_system(cred, KAUTH_SYSTEM_SYSVIPC, 540 1.63 elad KAUTH_REQ_SYSTEM_SYSVIPC_MSGQ_OVERSIZE, 541 1.63 elad KAUTH_ARG(msqbuf->msg_qbytes), 542 1.63 elad KAUTH_ARG(msqptr->msg_qbytes), NULL) != 0) { 543 1.48 ad error = EPERM; 544 1.48 ad break; 545 1.48 ad } 546 1.26 thorpej if (msqbuf->msg_qbytes > msginfo.msgmnb) { 547 1.26 thorpej MSG_PRINTF(("can't increase msg_qbytes beyond %d " 548 1.26 thorpej "(truncating)\n", msginfo.msgmnb)); 549 1.26 thorpej /* silently restrict qbytes to system limit */ 550 1.26 thorpej msqbuf->msg_qbytes = msginfo.msgmnb; 551 1.3 mycroft } 552 1.26 thorpej if (msqbuf->msg_qbytes == 0) { 553 1.20 christos MSG_PRINTF(("can't reduce msg_qbytes to 0\n")); 554 1.48 ad error = EINVAL; /* XXX non-standard errno! */ 555 1.48 ad break; 556 1.3 mycroft } 557 1.26 thorpej msqptr->msg_perm.uid = msqbuf->msg_perm.uid; 558 1.26 thorpej msqptr->msg_perm.gid = msqbuf->msg_perm.gid; 559 1.3 mycroft msqptr->msg_perm.mode = (msqptr->msg_perm.mode & ~0777) | 560 1.26 thorpej (msqbuf->msg_perm.mode & 0777); 561 1.26 thorpej msqptr->msg_qbytes = msqbuf->msg_qbytes; 562 1.43 kardel msqptr->msg_ctime = time_second; 563 1.3 mycroft break; 564 1.1 cgd 565 1.3 mycroft case IPC_STAT: 566 1.26 thorpej if ((error = ipcperm(cred, &msqptr->msg_perm, IPC_R))) { 567 1.20 christos MSG_PRINTF(("requester doesn't have read access\n")); 568 1.49 ad break; 569 1.3 mycroft } 570 1.73 mrg memset(msqbuf, 0, sizeof *msqbuf); 571 1.73 mrg msqbuf->msg_perm = msqptr->msg_perm; 572 1.73 mrg msqbuf->msg_perm.mode &= 0777; 573 1.73 mrg msqbuf->msg_qnum = msqptr->msg_qnum; 574 1.73 mrg msqbuf->msg_qbytes = msqptr->msg_qbytes; 575 1.73 mrg msqbuf->msg_lspid = msqptr->msg_lspid; 576 1.73 mrg msqbuf->msg_lrpid = msqptr->msg_lrpid; 577 1.73 mrg msqbuf->msg_stime = msqptr->msg_stime; 578 1.73 mrg msqbuf->msg_rtime = msqptr->msg_rtime; 579 1.73 mrg msqbuf->msg_ctime = msqptr->msg_ctime; 580 1.3 mycroft break; 581 1.1 cgd 582 1.3 mycroft default: 583 1.20 christos MSG_PRINTF(("invalid command %d\n", cmd)); 584 1.48 ad error = EINVAL; 585 1.48 ad break; 586 1.3 mycroft } 587 1.3 mycroft 588 1.50 rmind unlock: 589 1.48 ad mutex_exit(&msgmutex); 590 1.26 thorpej return (error); 591 1.1 cgd } 592 1.1 cgd 593 1.1 cgd int 594 1.54 dsl sys_msgget(struct lwp *l, const struct sys_msgget_args *uap, register_t *retval) 595 1.16 thorpej { 596 1.54 dsl /* { 597 1.10 cgd syscallarg(key_t) key; 598 1.10 cgd syscallarg(int) msgflg; 599 1.54 dsl } */ 600 1.48 ad int msqid, error = 0; 601 1.10 cgd int key = SCARG(uap, key); 602 1.10 cgd int msgflg = SCARG(uap, msgflg); 603 1.44 ad kauth_cred_t cred = l->l_cred; 604 1.26 thorpej struct msqid_ds *msqptr = NULL; 605 1.48 ad kmsq_t *msq; 606 1.48 ad 607 1.48 ad mutex_enter(&msgmutex); 608 1.1 cgd 609 1.20 christos MSG_PRINTF(("msgget(0x%x, 0%o)\n", key, msgflg)); 610 1.1 cgd 611 1.5 mycroft if (key != IPC_PRIVATE) { 612 1.5 mycroft for (msqid = 0; msqid < msginfo.msgmni; msqid++) { 613 1.48 ad msq = &msqs[msqid]; 614 1.48 ad msqptr = &msq->msq_u; 615 1.3 mycroft if (msqptr->msg_qbytes != 0 && 616 1.26 thorpej msqptr->msg_perm._key == key) 617 1.3 mycroft break; 618 1.3 mycroft } 619 1.3 mycroft if (msqid < msginfo.msgmni) { 620 1.20 christos MSG_PRINTF(("found public key\n")); 621 1.3 mycroft if ((msgflg & IPC_CREAT) && (msgflg & IPC_EXCL)) { 622 1.20 christos MSG_PRINTF(("not exclusive\n")); 623 1.48 ad error = EEXIST; 624 1.48 ad goto unlock; 625 1.3 mycroft } 626 1.26 thorpej if ((error = ipcperm(cred, &msqptr->msg_perm, 627 1.26 thorpej msgflg & 0700 ))) { 628 1.20 christos MSG_PRINTF(("requester doesn't have 0%o access\n", 629 1.20 christos msgflg & 0700)); 630 1.48 ad goto unlock; 631 1.3 mycroft } 632 1.5 mycroft goto found; 633 1.3 mycroft } 634 1.1 cgd } 635 1.1 cgd 636 1.20 christos MSG_PRINTF(("need to allocate the msqid_ds\n")); 637 1.5 mycroft if (key == IPC_PRIVATE || (msgflg & IPC_CREAT)) { 638 1.5 mycroft for (msqid = 0; msqid < msginfo.msgmni; msqid++) { 639 1.5 mycroft /* 640 1.5 mycroft * Look for an unallocated and unlocked msqid_ds. 641 1.5 mycroft * msqid_ds's can be locked by msgsnd or msgrcv while 642 1.5 mycroft * they are copying the message in/out. We can't 643 1.5 mycroft * re-use the entry until they release it. 644 1.5 mycroft */ 645 1.48 ad msq = &msqs[msqid]; 646 1.48 ad msqptr = &msq->msq_u; 647 1.5 mycroft if (msqptr->msg_qbytes == 0 && 648 1.5 mycroft (msqptr->msg_perm.mode & MSG_LOCKED) == 0) 649 1.5 mycroft break; 650 1.5 mycroft } 651 1.5 mycroft if (msqid == msginfo.msgmni) { 652 1.20 christos MSG_PRINTF(("no more msqid_ds's available\n")); 653 1.48 ad error = ENOSPC; 654 1.48 ad goto unlock; 655 1.5 mycroft } 656 1.20 christos MSG_PRINTF(("msqid %d is available\n", msqid)); 657 1.26 thorpej msqptr->msg_perm._key = key; 658 1.42 elad msqptr->msg_perm.cuid = kauth_cred_geteuid(cred); 659 1.42 elad msqptr->msg_perm.uid = kauth_cred_geteuid(cred); 660 1.42 elad msqptr->msg_perm.cgid = kauth_cred_getegid(cred); 661 1.42 elad msqptr->msg_perm.gid = kauth_cred_getegid(cred); 662 1.5 mycroft msqptr->msg_perm.mode = (msgflg & 0777); 663 1.5 mycroft /* Make sure that the returned msqid is unique */ 664 1.26 thorpej msqptr->msg_perm._seq++; 665 1.26 thorpej msqptr->_msg_first = NULL; 666 1.26 thorpej msqptr->_msg_last = NULL; 667 1.26 thorpej msqptr->_msg_cbytes = 0; 668 1.5 mycroft msqptr->msg_qnum = 0; 669 1.5 mycroft msqptr->msg_qbytes = msginfo.msgmnb; 670 1.5 mycroft msqptr->msg_lspid = 0; 671 1.5 mycroft msqptr->msg_lrpid = 0; 672 1.5 mycroft msqptr->msg_stime = 0; 673 1.5 mycroft msqptr->msg_rtime = 0; 674 1.43 kardel msqptr->msg_ctime = time_second; 675 1.5 mycroft } else { 676 1.20 christos MSG_PRINTF(("didn't find it and wasn't asked to create it\n")); 677 1.48 ad error = ENOENT; 678 1.48 ad goto unlock; 679 1.1 cgd } 680 1.1 cgd 681 1.50 rmind found: 682 1.3 mycroft /* Construct the unique msqid */ 683 1.3 mycroft *retval = IXSEQ_TO_IPCID(msqid, msqptr->msg_perm); 684 1.48 ad 685 1.50 rmind unlock: 686 1.50 rmind mutex_exit(&msgmutex); 687 1.48 ad return (error); 688 1.1 cgd } 689 1.1 cgd 690 1.1 cgd int 691 1.54 dsl sys_msgsnd(struct lwp *l, const struct sys_msgsnd_args *uap, register_t *retval) 692 1.16 thorpej { 693 1.54 dsl /* { 694 1.10 cgd syscallarg(int) msqid; 695 1.22 kleink syscallarg(const void *) msgp; 696 1.10 cgd syscallarg(size_t) msgsz; 697 1.10 cgd syscallarg(int) msgflg; 698 1.54 dsl } */ 699 1.41 cube 700 1.44 ad return msgsnd1(l, SCARG(uap, msqid), SCARG(uap, msgp), 701 1.41 cube SCARG(uap, msgsz), SCARG(uap, msgflg), sizeof(long), copyin); 702 1.41 cube } 703 1.41 cube 704 1.41 cube int 705 1.44 ad msgsnd1(struct lwp *l, int msqidr, const char *user_msgp, size_t msgsz, 706 1.41 cube int msgflg, size_t typesz, copyin_t fetch_type) 707 1.41 cube { 708 1.48 ad int segs_needed, error = 0, msqid; 709 1.44 ad kauth_cred_t cred = l->l_cred; 710 1.26 thorpej struct msqid_ds *msqptr; 711 1.26 thorpej struct __msg *msghdr; 712 1.48 ad kmsq_t *msq; 713 1.3 mycroft short next; 714 1.1 cgd 715 1.64 skrll MSG_PRINTF(("call to msgsnd(%d, %p, %lld, %d)\n", msqidr, 716 1.64 skrll user_msgp, (long long)msgsz, msgflg)); 717 1.60 njoly 718 1.60 njoly if ((ssize_t)msgsz < 0) 719 1.60 njoly return EINVAL; 720 1.60 njoly 721 1.53 rmind restart: 722 1.41 cube msqid = IPCID_TO_IX(msqidr); 723 1.1 cgd 724 1.48 ad mutex_enter(&msgmutex); 725 1.53 rmind /* In case of reallocation, we will wait for completion */ 726 1.53 rmind while (__predict_false(msg_realloc_state)) 727 1.53 rmind cv_wait(&msg_realloc_cv, &msgmutex); 728 1.48 ad 729 1.3 mycroft if (msqid < 0 || msqid >= msginfo.msgmni) { 730 1.20 christos MSG_PRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqid, 731 1.20 christos msginfo.msgmni)); 732 1.48 ad error = EINVAL; 733 1.48 ad goto unlock; 734 1.3 mycroft } 735 1.1 cgd 736 1.48 ad msq = &msqs[msqid]; 737 1.48 ad msqptr = &msq->msq_u; 738 1.48 ad 739 1.3 mycroft if (msqptr->msg_qbytes == 0) { 740 1.20 christos MSG_PRINTF(("no such message queue id\n")); 741 1.48 ad error = EINVAL; 742 1.48 ad goto unlock; 743 1.3 mycroft } 744 1.41 cube if (msqptr->msg_perm._seq != IPCID_TO_SEQ(msqidr)) { 745 1.20 christos MSG_PRINTF(("wrong sequence number\n")); 746 1.48 ad error = EINVAL; 747 1.48 ad goto unlock; 748 1.3 mycroft } 749 1.1 cgd 750 1.26 thorpej if ((error = ipcperm(cred, &msqptr->msg_perm, IPC_W))) { 751 1.20 christos MSG_PRINTF(("requester doesn't have write access\n")); 752 1.48 ad goto unlock; 753 1.3 mycroft } 754 1.1 cgd 755 1.3 mycroft segs_needed = (msgsz + msginfo.msgssz - 1) / msginfo.msgssz; 756 1.34 nathanw MSG_PRINTF(("msgsz=%lld, msgssz=%d, segs_needed=%d\n", 757 1.34 nathanw (long long)msgsz, msginfo.msgssz, segs_needed)); 758 1.3 mycroft for (;;) { 759 1.3 mycroft int need_more_resources = 0; 760 1.1 cgd 761 1.3 mycroft /* 762 1.18 christos * check msgsz [cannot be negative since it is unsigned] 763 1.3 mycroft * (inside this loop in case msg_qbytes changes while we sleep) 764 1.3 mycroft */ 765 1.1 cgd 766 1.18 christos if (msgsz > msqptr->msg_qbytes) { 767 1.20 christos MSG_PRINTF(("msgsz > msqptr->msg_qbytes\n")); 768 1.48 ad error = EINVAL; 769 1.48 ad goto unlock; 770 1.3 mycroft } 771 1.1 cgd 772 1.3 mycroft if (msqptr->msg_perm.mode & MSG_LOCKED) { 773 1.20 christos MSG_PRINTF(("msqid is locked\n")); 774 1.3 mycroft need_more_resources = 1; 775 1.3 mycroft } 776 1.26 thorpej if (msgsz + msqptr->_msg_cbytes > msqptr->msg_qbytes) { 777 1.20 christos MSG_PRINTF(("msgsz + msg_cbytes > msg_qbytes\n")); 778 1.3 mycroft need_more_resources = 1; 779 1.3 mycroft } 780 1.3 mycroft if (segs_needed > nfree_msgmaps) { 781 1.20 christos MSG_PRINTF(("segs_needed > nfree_msgmaps\n")); 782 1.3 mycroft need_more_resources = 1; 783 1.3 mycroft } 784 1.3 mycroft if (free_msghdrs == NULL) { 785 1.20 christos MSG_PRINTF(("no more msghdrs\n")); 786 1.3 mycroft need_more_resources = 1; 787 1.3 mycroft } 788 1.1 cgd 789 1.3 mycroft if (need_more_resources) { 790 1.3 mycroft int we_own_it; 791 1.1 cgd 792 1.3 mycroft if ((msgflg & IPC_NOWAIT) != 0) { 793 1.26 thorpej MSG_PRINTF(("need more resources but caller " 794 1.26 thorpej "doesn't want to wait\n")); 795 1.48 ad error = EAGAIN; 796 1.48 ad goto unlock; 797 1.3 mycroft } 798 1.1 cgd 799 1.3 mycroft if ((msqptr->msg_perm.mode & MSG_LOCKED) != 0) { 800 1.20 christos MSG_PRINTF(("we don't own the msqid_ds\n")); 801 1.3 mycroft we_own_it = 0; 802 1.3 mycroft } else { 803 1.3 mycroft /* Force later arrivals to wait for our 804 1.3 mycroft request */ 805 1.20 christos MSG_PRINTF(("we own the msqid_ds\n")); 806 1.3 mycroft msqptr->msg_perm.mode |= MSG_LOCKED; 807 1.3 mycroft we_own_it = 1; 808 1.3 mycroft } 809 1.51 rmind 810 1.51 rmind msg_waiters++; 811 1.20 christos MSG_PRINTF(("goodnight\n")); 812 1.48 ad error = cv_wait_sig(&msq->msq_cv, &msgmutex); 813 1.26 thorpej MSG_PRINTF(("good morning, error=%d\n", error)); 814 1.51 rmind msg_waiters--; 815 1.51 rmind 816 1.53 rmind if (we_own_it) 817 1.53 rmind msqptr->msg_perm.mode &= ~MSG_LOCKED; 818 1.53 rmind 819 1.53 rmind /* 820 1.53 rmind * In case of such state, notify reallocator and 821 1.53 rmind * restart the call. 822 1.53 rmind */ 823 1.53 rmind if (msg_realloc_state) { 824 1.52 yamt cv_broadcast(&msg_realloc_cv); 825 1.53 rmind mutex_exit(&msgmutex); 826 1.53 rmind goto restart; 827 1.53 rmind } 828 1.51 rmind 829 1.53 rmind if (error != 0) { 830 1.26 thorpej MSG_PRINTF(("msgsnd: interrupted system " 831 1.26 thorpej "call\n")); 832 1.48 ad error = EINTR; 833 1.48 ad goto unlock; 834 1.3 mycroft } 835 1.1 cgd 836 1.3 mycroft /* 837 1.3 mycroft * Make sure that the msq queue still exists 838 1.3 mycroft */ 839 1.1 cgd 840 1.3 mycroft if (msqptr->msg_qbytes == 0) { 841 1.20 christos MSG_PRINTF(("msqid deleted\n")); 842 1.48 ad error = EIDRM; 843 1.48 ad goto unlock; 844 1.3 mycroft } 845 1.3 mycroft } else { 846 1.20 christos MSG_PRINTF(("got all the resources that we need\n")); 847 1.3 mycroft break; 848 1.3 mycroft } 849 1.1 cgd } 850 1.1 cgd 851 1.3 mycroft /* 852 1.3 mycroft * We have the resources that we need. 853 1.3 mycroft * Make sure! 854 1.3 mycroft */ 855 1.1 cgd 856 1.50 rmind KASSERT((msqptr->msg_perm.mode & MSG_LOCKED) == 0); 857 1.50 rmind KASSERT(segs_needed <= nfree_msgmaps); 858 1.50 rmind KASSERT(msgsz + msqptr->_msg_cbytes <= msqptr->msg_qbytes); 859 1.50 rmind KASSERT(free_msghdrs != NULL); 860 1.1 cgd 861 1.3 mycroft /* 862 1.3 mycroft * Re-lock the msqid_ds in case we page-fault when copying in the 863 1.3 mycroft * message 864 1.3 mycroft */ 865 1.1 cgd 866 1.50 rmind KASSERT((msqptr->msg_perm.mode & MSG_LOCKED) == 0); 867 1.3 mycroft msqptr->msg_perm.mode |= MSG_LOCKED; 868 1.1 cgd 869 1.3 mycroft /* 870 1.3 mycroft * Allocate a message header 871 1.3 mycroft */ 872 1.1 cgd 873 1.3 mycroft msghdr = free_msghdrs; 874 1.3 mycroft free_msghdrs = msghdr->msg_next; 875 1.3 mycroft msghdr->msg_spot = -1; 876 1.3 mycroft msghdr->msg_ts = msgsz; 877 1.1 cgd 878 1.3 mycroft /* 879 1.3 mycroft * Allocate space for the message 880 1.3 mycroft */ 881 1.1 cgd 882 1.3 mycroft while (segs_needed > 0) { 883 1.50 rmind KASSERT(nfree_msgmaps > 0); 884 1.50 rmind KASSERT(free_msgmaps != -1); 885 1.50 rmind KASSERT(free_msgmaps < msginfo.msgseg); 886 1.50 rmind 887 1.3 mycroft next = free_msgmaps; 888 1.20 christos MSG_PRINTF(("allocating segment %d to message\n", next)); 889 1.3 mycroft free_msgmaps = msgmaps[next].next; 890 1.5 mycroft nfree_msgmaps--; 891 1.3 mycroft msgmaps[next].next = msghdr->msg_spot; 892 1.3 mycroft msghdr->msg_spot = next; 893 1.5 mycroft segs_needed--; 894 1.1 cgd } 895 1.1 cgd 896 1.3 mycroft /* 897 1.3 mycroft * Copy in the message type 898 1.3 mycroft */ 899 1.48 ad mutex_exit(&msgmutex); 900 1.48 ad error = (*fetch_type)(user_msgp, &msghdr->msg_type, typesz); 901 1.48 ad mutex_enter(&msgmutex); 902 1.48 ad if (error != 0) { 903 1.26 thorpej MSG_PRINTF(("error %d copying the message type\n", error)); 904 1.3 mycroft msg_freehdr(msghdr); 905 1.3 mycroft msqptr->msg_perm.mode &= ~MSG_LOCKED; 906 1.48 ad cv_broadcast(&msq->msq_cv); 907 1.48 ad goto unlock; 908 1.3 mycroft } 909 1.41 cube user_msgp += typesz; 910 1.1 cgd 911 1.3 mycroft /* 912 1.3 mycroft * Validate the message type 913 1.3 mycroft */ 914 1.1 cgd 915 1.3 mycroft if (msghdr->msg_type < 1) { 916 1.3 mycroft msg_freehdr(msghdr); 917 1.3 mycroft msqptr->msg_perm.mode &= ~MSG_LOCKED; 918 1.48 ad cv_broadcast(&msq->msq_cv); 919 1.34 nathanw MSG_PRINTF(("mtype (%ld) < 1\n", msghdr->msg_type)); 920 1.57 njoly error = EINVAL; 921 1.48 ad goto unlock; 922 1.3 mycroft } 923 1.1 cgd 924 1.3 mycroft /* 925 1.3 mycroft * Copy in the message body 926 1.3 mycroft */ 927 1.3 mycroft 928 1.3 mycroft next = msghdr->msg_spot; 929 1.3 mycroft while (msgsz > 0) { 930 1.3 mycroft size_t tlen; 931 1.50 rmind KASSERT(next > -1); 932 1.50 rmind KASSERT(next < msginfo.msgseg); 933 1.50 rmind 934 1.3 mycroft if (msgsz > msginfo.msgssz) 935 1.3 mycroft tlen = msginfo.msgssz; 936 1.3 mycroft else 937 1.3 mycroft tlen = msgsz; 938 1.48 ad mutex_exit(&msgmutex); 939 1.48 ad error = copyin(user_msgp, &msgpool[next * msginfo.msgssz], tlen); 940 1.48 ad mutex_enter(&msgmutex); 941 1.48 ad if (error != 0) { 942 1.26 thorpej MSG_PRINTF(("error %d copying in message segment\n", 943 1.26 thorpej error)); 944 1.3 mycroft msg_freehdr(msghdr); 945 1.3 mycroft msqptr->msg_perm.mode &= ~MSG_LOCKED; 946 1.48 ad cv_broadcast(&msq->msq_cv); 947 1.48 ad goto unlock; 948 1.3 mycroft } 949 1.3 mycroft msgsz -= tlen; 950 1.3 mycroft user_msgp += tlen; 951 1.3 mycroft next = msgmaps[next].next; 952 1.1 cgd } 953 1.50 rmind KASSERT(next == -1); 954 1.1 cgd 955 1.3 mycroft /* 956 1.3 mycroft * We've got the message. Unlock the msqid_ds. 957 1.3 mycroft */ 958 1.1 cgd 959 1.3 mycroft msqptr->msg_perm.mode &= ~MSG_LOCKED; 960 1.1 cgd 961 1.3 mycroft /* 962 1.3 mycroft * Make sure that the msqid_ds is still allocated. 963 1.3 mycroft */ 964 1.1 cgd 965 1.3 mycroft if (msqptr->msg_qbytes == 0) { 966 1.3 mycroft msg_freehdr(msghdr); 967 1.48 ad cv_broadcast(&msq->msq_cv); 968 1.48 ad error = EIDRM; 969 1.48 ad goto unlock; 970 1.3 mycroft } 971 1.3 mycroft 972 1.3 mycroft /* 973 1.3 mycroft * Put the message into the queue 974 1.3 mycroft */ 975 1.1 cgd 976 1.26 thorpej if (msqptr->_msg_first == NULL) { 977 1.26 thorpej msqptr->_msg_first = msghdr; 978 1.26 thorpej msqptr->_msg_last = msghdr; 979 1.3 mycroft } else { 980 1.26 thorpej msqptr->_msg_last->msg_next = msghdr; 981 1.26 thorpej msqptr->_msg_last = msghdr; 982 1.3 mycroft } 983 1.26 thorpej msqptr->_msg_last->msg_next = NULL; 984 1.3 mycroft 985 1.26 thorpej msqptr->_msg_cbytes += msghdr->msg_ts; 986 1.5 mycroft msqptr->msg_qnum++; 987 1.44 ad msqptr->msg_lspid = l->l_proc->p_pid; 988 1.43 kardel msqptr->msg_stime = time_second; 989 1.3 mycroft 990 1.48 ad cv_broadcast(&msq->msq_cv); 991 1.48 ad 992 1.50 rmind unlock: 993 1.50 rmind mutex_exit(&msgmutex); 994 1.48 ad return error; 995 1.1 cgd } 996 1.1 cgd 997 1.1 cgd int 998 1.54 dsl sys_msgrcv(struct lwp *l, const struct sys_msgrcv_args *uap, register_t *retval) 999 1.16 thorpej { 1000 1.54 dsl /* { 1001 1.10 cgd syscallarg(int) msqid; 1002 1.10 cgd syscallarg(void *) msgp; 1003 1.10 cgd syscallarg(size_t) msgsz; 1004 1.10 cgd syscallarg(long) msgtyp; 1005 1.10 cgd syscallarg(int) msgflg; 1006 1.54 dsl } */ 1007 1.41 cube 1008 1.44 ad return msgrcv1(l, SCARG(uap, msqid), SCARG(uap, msgp), 1009 1.41 cube SCARG(uap, msgsz), SCARG(uap, msgtyp), SCARG(uap, msgflg), 1010 1.41 cube sizeof(long), copyout, retval); 1011 1.41 cube } 1012 1.41 cube 1013 1.41 cube int 1014 1.44 ad msgrcv1(struct lwp *l, int msqidr, char *user_msgp, size_t msgsz, long msgtyp, 1015 1.41 cube int msgflg, size_t typesz, copyout_t put_type, register_t *retval) 1016 1.41 cube { 1017 1.3 mycroft size_t len; 1018 1.44 ad kauth_cred_t cred = l->l_cred; 1019 1.27 augustss struct msqid_ds *msqptr; 1020 1.27 augustss struct __msg *msghdr; 1021 1.48 ad int error = 0, msqid; 1022 1.48 ad kmsq_t *msq; 1023 1.3 mycroft short next; 1024 1.1 cgd 1025 1.64 skrll MSG_PRINTF(("call to msgrcv(%d, %p, %lld, %ld, %d)\n", msqidr, 1026 1.34 nathanw user_msgp, (long long)msgsz, msgtyp, msgflg)); 1027 1.60 njoly 1028 1.60 njoly if ((ssize_t)msgsz < 0) 1029 1.60 njoly return EINVAL; 1030 1.60 njoly 1031 1.53 rmind restart: 1032 1.41 cube msqid = IPCID_TO_IX(msqidr); 1033 1.1 cgd 1034 1.48 ad mutex_enter(&msgmutex); 1035 1.53 rmind /* In case of reallocation, we will wait for completion */ 1036 1.53 rmind while (__predict_false(msg_realloc_state)) 1037 1.53 rmind cv_wait(&msg_realloc_cv, &msgmutex); 1038 1.48 ad 1039 1.3 mycroft if (msqid < 0 || msqid >= msginfo.msgmni) { 1040 1.20 christos MSG_PRINTF(("msqid (%d) out of range (0<=msqid<%d)\n", msqid, 1041 1.20 christos msginfo.msgmni)); 1042 1.48 ad error = EINVAL; 1043 1.48 ad goto unlock; 1044 1.3 mycroft } 1045 1.1 cgd 1046 1.48 ad msq = &msqs[msqid]; 1047 1.48 ad msqptr = &msq->msq_u; 1048 1.48 ad 1049 1.3 mycroft if (msqptr->msg_qbytes == 0) { 1050 1.20 christos MSG_PRINTF(("no such message queue id\n")); 1051 1.48 ad error = EINVAL; 1052 1.48 ad goto unlock; 1053 1.3 mycroft } 1054 1.41 cube if (msqptr->msg_perm._seq != IPCID_TO_SEQ(msqidr)) { 1055 1.20 christos MSG_PRINTF(("wrong sequence number\n")); 1056 1.48 ad error = EINVAL; 1057 1.48 ad goto unlock; 1058 1.3 mycroft } 1059 1.1 cgd 1060 1.26 thorpej if ((error = ipcperm(cred, &msqptr->msg_perm, IPC_R))) { 1061 1.20 christos MSG_PRINTF(("requester doesn't have read access\n")); 1062 1.48 ad goto unlock; 1063 1.3 mycroft } 1064 1.1 cgd 1065 1.3 mycroft msghdr = NULL; 1066 1.3 mycroft while (msghdr == NULL) { 1067 1.3 mycroft if (msgtyp == 0) { 1068 1.26 thorpej msghdr = msqptr->_msg_first; 1069 1.3 mycroft if (msghdr != NULL) { 1070 1.3 mycroft if (msgsz < msghdr->msg_ts && 1071 1.3 mycroft (msgflg & MSG_NOERROR) == 0) { 1072 1.50 rmind MSG_PRINTF(("first msg on the queue " 1073 1.50 rmind "is too big (want %lld, got %d)\n", 1074 1.34 nathanw (long long)msgsz, msghdr->msg_ts)); 1075 1.48 ad error = E2BIG; 1076 1.48 ad goto unlock; 1077 1.3 mycroft } 1078 1.26 thorpej if (msqptr->_msg_first == msqptr->_msg_last) { 1079 1.26 thorpej msqptr->_msg_first = NULL; 1080 1.26 thorpej msqptr->_msg_last = NULL; 1081 1.3 mycroft } else { 1082 1.26 thorpej msqptr->_msg_first = msghdr->msg_next; 1083 1.50 rmind KASSERT(msqptr->_msg_first != NULL); 1084 1.3 mycroft } 1085 1.3 mycroft } 1086 1.3 mycroft } else { 1087 1.26 thorpej struct __msg *previous; 1088 1.26 thorpej struct __msg **prev; 1089 1.1 cgd 1090 1.26 thorpej for (previous = NULL, prev = &msqptr->_msg_first; 1091 1.12 mycroft (msghdr = *prev) != NULL; 1092 1.12 mycroft previous = msghdr, prev = &msghdr->msg_next) { 1093 1.3 mycroft /* 1094 1.3 mycroft * Is this message's type an exact match or is 1095 1.3 mycroft * this message's type less than or equal to 1096 1.3 mycroft * the absolute value of a negative msgtyp? 1097 1.3 mycroft * Note that the second half of this test can 1098 1.3 mycroft * NEVER be true if msgtyp is positive since 1099 1.3 mycroft * msg_type is always positive! 1100 1.3 mycroft */ 1101 1.3 mycroft 1102 1.50 rmind if (msgtyp != msghdr->msg_type && 1103 1.76 kamil msgtyp != LONG_MIN && 1104 1.50 rmind msghdr->msg_type > -msgtyp) 1105 1.50 rmind continue; 1106 1.50 rmind 1107 1.50 rmind MSG_PRINTF(("found message type %ld, requested %ld\n", 1108 1.50 rmind msghdr->msg_type, msgtyp)); 1109 1.50 rmind if (msgsz < msghdr->msg_ts && 1110 1.50 rmind (msgflg & MSG_NOERROR) == 0) { 1111 1.50 rmind MSG_PRINTF(("requested message on the queue " 1112 1.50 rmind "is too big (want %lld, got %d)\n", 1113 1.50 rmind (long long)msgsz, msghdr->msg_ts)); 1114 1.50 rmind error = E2BIG; 1115 1.50 rmind goto unlock; 1116 1.50 rmind } 1117 1.50 rmind *prev = msghdr->msg_next; 1118 1.50 rmind if (msghdr != msqptr->_msg_last) 1119 1.3 mycroft break; 1120 1.50 rmind if (previous == NULL) { 1121 1.50 rmind KASSERT(prev == &msqptr->_msg_first); 1122 1.50 rmind msqptr->_msg_first = NULL; 1123 1.50 rmind msqptr->_msg_last = NULL; 1124 1.50 rmind } else { 1125 1.50 rmind KASSERT(prev != &msqptr->_msg_first); 1126 1.50 rmind msqptr->_msg_last = previous; 1127 1.3 mycroft } 1128 1.50 rmind break; 1129 1.3 mycroft } 1130 1.1 cgd } 1131 1.1 cgd 1132 1.3 mycroft /* 1133 1.3 mycroft * We've either extracted the msghdr for the appropriate 1134 1.3 mycroft * message or there isn't one. 1135 1.3 mycroft * If there is one then bail out of this loop. 1136 1.3 mycroft */ 1137 1.3 mycroft if (msghdr != NULL) 1138 1.3 mycroft break; 1139 1.1 cgd 1140 1.1 cgd /* 1141 1.3 mycroft * Hmph! No message found. Does the user want to wait? 1142 1.1 cgd */ 1143 1.1 cgd 1144 1.3 mycroft if ((msgflg & IPC_NOWAIT) != 0) { 1145 1.34 nathanw MSG_PRINTF(("no appropriate message found (msgtyp=%ld)\n", 1146 1.20 christos msgtyp)); 1147 1.48 ad error = ENOMSG; 1148 1.48 ad goto unlock; 1149 1.3 mycroft } 1150 1.1 cgd 1151 1.3 mycroft /* 1152 1.3 mycroft * Wait for something to happen 1153 1.3 mycroft */ 1154 1.1 cgd 1155 1.51 rmind msg_waiters++; 1156 1.20 christos MSG_PRINTF(("msgrcv: goodnight\n")); 1157 1.48 ad error = cv_wait_sig(&msq->msq_cv, &msgmutex); 1158 1.26 thorpej MSG_PRINTF(("msgrcv: good morning (error=%d)\n", error)); 1159 1.51 rmind msg_waiters--; 1160 1.1 cgd 1161 1.53 rmind /* 1162 1.53 rmind * In case of such state, notify reallocator and 1163 1.53 rmind * restart the call. 1164 1.53 rmind */ 1165 1.53 rmind if (msg_realloc_state) { 1166 1.52 yamt cv_broadcast(&msg_realloc_cv); 1167 1.53 rmind mutex_exit(&msgmutex); 1168 1.53 rmind goto restart; 1169 1.53 rmind } 1170 1.51 rmind 1171 1.53 rmind if (error != 0) { 1172 1.26 thorpej MSG_PRINTF(("msgsnd: interrupted system call\n")); 1173 1.48 ad error = EINTR; 1174 1.48 ad goto unlock; 1175 1.3 mycroft } 1176 1.1 cgd 1177 1.3 mycroft /* 1178 1.3 mycroft * Make sure that the msq queue still exists 1179 1.3 mycroft */ 1180 1.1 cgd 1181 1.3 mycroft if (msqptr->msg_qbytes == 0 || 1182 1.41 cube msqptr->msg_perm._seq != IPCID_TO_SEQ(msqidr)) { 1183 1.20 christos MSG_PRINTF(("msqid deleted\n")); 1184 1.48 ad error = EIDRM; 1185 1.48 ad goto unlock; 1186 1.3 mycroft } 1187 1.1 cgd } 1188 1.1 cgd 1189 1.3 mycroft /* 1190 1.3 mycroft * Return the message to the user. 1191 1.3 mycroft * 1192 1.3 mycroft * First, do the bookkeeping (before we risk being interrupted). 1193 1.3 mycroft */ 1194 1.1 cgd 1195 1.26 thorpej msqptr->_msg_cbytes -= msghdr->msg_ts; 1196 1.5 mycroft msqptr->msg_qnum--; 1197 1.44 ad msqptr->msg_lrpid = l->l_proc->p_pid; 1198 1.43 kardel msqptr->msg_rtime = time_second; 1199 1.1 cgd 1200 1.3 mycroft /* 1201 1.3 mycroft * Make msgsz the actual amount that we'll be returning. 1202 1.3 mycroft * Note that this effectively truncates the message if it is too long 1203 1.3 mycroft * (since msgsz is never increased). 1204 1.3 mycroft */ 1205 1.1 cgd 1206 1.34 nathanw MSG_PRINTF(("found a message, msgsz=%lld, msg_ts=%d\n", 1207 1.34 nathanw (long long)msgsz, msghdr->msg_ts)); 1208 1.3 mycroft if (msgsz > msghdr->msg_ts) 1209 1.3 mycroft msgsz = msghdr->msg_ts; 1210 1.1 cgd 1211 1.3 mycroft /* 1212 1.3 mycroft * Return the type to the user. 1213 1.3 mycroft */ 1214 1.48 ad mutex_exit(&msgmutex); 1215 1.41 cube error = (*put_type)(&msghdr->msg_type, user_msgp, typesz); 1216 1.48 ad mutex_enter(&msgmutex); 1217 1.26 thorpej if (error != 0) { 1218 1.26 thorpej MSG_PRINTF(("error (%d) copying out message type\n", error)); 1219 1.3 mycroft msg_freehdr(msghdr); 1220 1.48 ad cv_broadcast(&msq->msq_cv); 1221 1.48 ad goto unlock; 1222 1.3 mycroft } 1223 1.41 cube user_msgp += typesz; 1224 1.3 mycroft 1225 1.3 mycroft /* 1226 1.3 mycroft * Return the segments to the user 1227 1.3 mycroft */ 1228 1.1 cgd 1229 1.3 mycroft next = msghdr->msg_spot; 1230 1.3 mycroft for (len = 0; len < msgsz; len += msginfo.msgssz) { 1231 1.3 mycroft size_t tlen; 1232 1.50 rmind KASSERT(next > -1); 1233 1.50 rmind KASSERT(next < msginfo.msgseg); 1234 1.3 mycroft 1235 1.25 mrg if (msgsz - len > msginfo.msgssz) 1236 1.3 mycroft tlen = msginfo.msgssz; 1237 1.3 mycroft else 1238 1.25 mrg tlen = msgsz - len; 1239 1.48 ad mutex_exit(&msgmutex); 1240 1.61 njoly error = copyout(&msgpool[next * msginfo.msgssz], 1241 1.3 mycroft user_msgp, tlen); 1242 1.48 ad mutex_enter(&msgmutex); 1243 1.26 thorpej if (error != 0) { 1244 1.20 christos MSG_PRINTF(("error (%d) copying out message segment\n", 1245 1.26 thorpej error)); 1246 1.3 mycroft msg_freehdr(msghdr); 1247 1.48 ad cv_broadcast(&msq->msq_cv); 1248 1.48 ad goto unlock; 1249 1.3 mycroft } 1250 1.3 mycroft user_msgp += tlen; 1251 1.3 mycroft next = msgmaps[next].next; 1252 1.1 cgd } 1253 1.1 cgd 1254 1.3 mycroft /* 1255 1.3 mycroft * Done, return the actual number of bytes copied out. 1256 1.3 mycroft */ 1257 1.1 cgd 1258 1.3 mycroft msg_freehdr(msghdr); 1259 1.48 ad cv_broadcast(&msq->msq_cv); 1260 1.3 mycroft *retval = msgsz; 1261 1.48 ad 1262 1.50 rmind unlock: 1263 1.50 rmind mutex_exit(&msgmutex); 1264 1.48 ad return error; 1265 1.1 cgd } 1266 1.51 rmind 1267 1.51 rmind /* 1268 1.51 rmind * Sysctl initialization and nodes. 1269 1.51 rmind */ 1270 1.51 rmind 1271 1.51 rmind static int 1272 1.51 rmind sysctl_ipc_msgmni(SYSCTLFN_ARGS) 1273 1.51 rmind { 1274 1.51 rmind int newsize, error; 1275 1.51 rmind struct sysctlnode node; 1276 1.51 rmind node = *rnode; 1277 1.51 rmind node.sysctl_data = &newsize; 1278 1.51 rmind 1279 1.51 rmind newsize = msginfo.msgmni; 1280 1.51 rmind error = sysctl_lookup(SYSCTLFN_CALL(&node)); 1281 1.51 rmind if (error || newp == NULL) 1282 1.51 rmind return error; 1283 1.51 rmind 1284 1.55 ad sysctl_unlock(); 1285 1.55 ad error = msgrealloc(newsize, msginfo.msgseg); 1286 1.55 ad sysctl_relock(); 1287 1.55 ad return error; 1288 1.51 rmind } 1289 1.51 rmind 1290 1.51 rmind static int 1291 1.51 rmind sysctl_ipc_msgseg(SYSCTLFN_ARGS) 1292 1.51 rmind { 1293 1.51 rmind int newsize, error; 1294 1.51 rmind struct sysctlnode node; 1295 1.51 rmind node = *rnode; 1296 1.51 rmind node.sysctl_data = &newsize; 1297 1.51 rmind 1298 1.51 rmind newsize = msginfo.msgseg; 1299 1.51 rmind error = sysctl_lookup(SYSCTLFN_CALL(&node)); 1300 1.51 rmind if (error || newp == NULL) 1301 1.51 rmind return error; 1302 1.51 rmind 1303 1.55 ad sysctl_unlock(); 1304 1.55 ad error = msgrealloc(msginfo.msgmni, newsize); 1305 1.55 ad sysctl_relock(); 1306 1.55 ad return error; 1307 1.51 rmind } 1308 1.51 rmind 1309 1.51 rmind SYSCTL_SETUP(sysctl_ipc_msg_setup, "sysctl kern.ipc subtree setup") 1310 1.51 rmind { 1311 1.51 rmind const struct sysctlnode *node = NULL; 1312 1.51 rmind 1313 1.51 rmind sysctl_createv(clog, 0, NULL, &node, 1314 1.51 rmind CTLFLAG_PERMANENT, 1315 1.51 rmind CTLTYPE_NODE, "ipc", 1316 1.51 rmind SYSCTL_DESCR("SysV IPC options"), 1317 1.51 rmind NULL, 0, NULL, 0, 1318 1.51 rmind CTL_KERN, KERN_SYSVIPC, CTL_EOL); 1319 1.51 rmind 1320 1.51 rmind if (node == NULL) 1321 1.51 rmind return; 1322 1.51 rmind 1323 1.51 rmind sysctl_createv(clog, 0, &node, NULL, 1324 1.51 rmind CTLFLAG_PERMANENT | CTLFLAG_READWRITE, 1325 1.51 rmind CTLTYPE_INT, "msgmni", 1326 1.51 rmind SYSCTL_DESCR("Max number of message queue identifiers"), 1327 1.51 rmind sysctl_ipc_msgmni, 0, &msginfo.msgmni, 0, 1328 1.51 rmind CTL_CREATE, CTL_EOL); 1329 1.51 rmind sysctl_createv(clog, 0, &node, NULL, 1330 1.51 rmind CTLFLAG_PERMANENT | CTLFLAG_READWRITE, 1331 1.51 rmind CTLTYPE_INT, "msgseg", 1332 1.51 rmind SYSCTL_DESCR("Max number of number of message segments"), 1333 1.51 rmind sysctl_ipc_msgseg, 0, &msginfo.msgseg, 0, 1334 1.51 rmind CTL_CREATE, CTL_EOL); 1335 1.51 rmind } 1336