sysv_shm.c revision 12819
1/* $Id: sysv_shm.c,v 1.13 1995/12/07 12:46:55 davidg Exp $ */ 2/* $NetBSD: sysv_shm.c,v 1.23 1994/07/04 23:25:12 glass Exp $ */ 3 4/* 5 * Copyright (c) 1994 Adam Glass and Charles Hannum. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by Adam Glass and Charles 18 * Hannum. 19 * 4. The names of the authors may not be used to endorse or promote products 20 * derived from this software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 27 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 31 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34#include <sys/param.h> 35#include <sys/systm.h> 36#include <sys/sysproto.h> 37#include <sys/kernel.h> 38#include <sys/shm.h> 39#include <sys/proc.h> 40#include <sys/malloc.h> 41#include <sys/mman.h> 42#include <sys/stat.h> 43#include <sys/sysent.h> 44 45#include <vm/vm.h> 46#include <vm/vm_param.h> 47#include <vm/vm_prot.h> 48#include <vm/lock.h> 49#include <vm/pmap.h> 50#include <vm/vm_map.h> 51#include <vm/vm_kern.h> 52#include <vm/vm_extern.h> 53 54struct shmat_args; 55extern int shmat __P((struct proc *p, struct shmat_args *uap, int *retval)); 56struct shmctl_args; 57extern int shmctl __P((struct proc *p, struct shmctl_args *uap, int *retval)); 58struct shmdt_args; 59extern int shmdt __P((struct proc *p, struct shmdt_args *uap, int *retval)); 60struct shmget_args; 61extern int shmget __P((struct proc *p, struct shmget_args *uap, int *retval)); 62 63static void shminit __P((void *)); 64SYSINIT(sysv_shm, SI_SUB_SYSV_SHM, SI_ORDER_FIRST, shminit, NULL) 65 66struct oshmctl_args; 67static int oshmctl __P((struct proc *p, struct oshmctl_args *uap, int *retval)); 68static int shmget_allocate_segment __P((struct proc *p, struct shmget_args *uap, int mode, int *retval)); 69static int shmget_existing __P((struct proc *p, struct shmget_args *uap, int mode, int segnum, int *retval)); 70 71/* XXX casting to (sy_call_t *) is bogus, as usual. */ 72sy_call_t *shmcalls[] = { 73 (sy_call_t *)shmat, (sy_call_t *)oshmctl, 74 (sy_call_t *)shmdt, (sy_call_t *)shmget, 75 (sy_call_t *)shmctl 76}; 77 78#define SHMSEG_FREE 0x0200 79#define SHMSEG_REMOVED 0x0400 80#define SHMSEG_ALLOCATED 0x0800 81#define SHMSEG_WANTED 0x1000 82 83static vm_map_t sysvshm_map; 84static int shm_last_free, shm_nused, shm_committed; 85struct shmid_ds *shmsegs; 86 87struct shm_handle { 88 vm_offset_t kva; 89}; 90 91struct shmmap_state { 92 vm_offset_t va; 93 int shmid; 94}; 95 96static void shm_deallocate_segment __P((struct shmid_ds *)); 97static int shm_find_segment_by_key __P((key_t)); 98static struct shmid_ds *shm_find_segment_by_shmid __P((int)); 99static int shm_delete_mapping __P((struct proc *, struct shmmap_state *)); 100 101static int 102shm_find_segment_by_key(key) 103 key_t key; 104{ 105 int i; 106 107 for (i = 0; i < shminfo.shmmni; i++) 108 if ((shmsegs[i].shm_perm.mode & SHMSEG_ALLOCATED) && 109 shmsegs[i].shm_perm.key == key) 110 return i; 111 return -1; 112} 113 114static struct shmid_ds * 115shm_find_segment_by_shmid(shmid) 116 int shmid; 117{ 118 int segnum; 119 struct shmid_ds *shmseg; 120 121 segnum = IPCID_TO_IX(shmid); 122 if (segnum < 0 || segnum >= shminfo.shmmni) 123 return NULL; 124 shmseg = &shmsegs[segnum]; 125 if ((shmseg->shm_perm.mode & (SHMSEG_ALLOCATED | SHMSEG_REMOVED)) 126 != SHMSEG_ALLOCATED || 127 shmseg->shm_perm.seq != IPCID_TO_SEQ(shmid)) 128 return NULL; 129 return shmseg; 130} 131 132static void 133shm_deallocate_segment(shmseg) 134 struct shmid_ds *shmseg; 135{ 136 struct shm_handle *shm_handle; 137 size_t size; 138 139 shm_handle = shmseg->shm_internal; 140 size = (shmseg->shm_segsz + CLOFSET) & ~CLOFSET; 141 (void) vm_map_remove(sysvshm_map, shm_handle->kva, shm_handle->kva + size); 142 free((caddr_t)shm_handle, M_SHM); 143 shmseg->shm_internal = NULL; 144 shm_committed -= btoc(size); 145 shm_nused--; 146 shmseg->shm_perm.mode = SHMSEG_FREE; 147} 148 149static int 150shm_delete_mapping(p, shmmap_s) 151 struct proc *p; 152 struct shmmap_state *shmmap_s; 153{ 154 struct shmid_ds *shmseg; 155 int segnum, result; 156 size_t size; 157 158 segnum = IPCID_TO_IX(shmmap_s->shmid); 159 shmseg = &shmsegs[segnum]; 160 size = (shmseg->shm_segsz + CLOFSET) & ~CLOFSET; 161 result = vm_map_remove(&p->p_vmspace->vm_map, shmmap_s->va, shmmap_s->va + size); 162 if (result != KERN_SUCCESS) 163 return EINVAL; 164 shmmap_s->shmid = -1; 165 shmseg->shm_dtime = time.tv_sec; 166 if ((--shmseg->shm_nattch <= 0) && 167 (shmseg->shm_perm.mode & SHMSEG_REMOVED)) { 168 shm_deallocate_segment(shmseg); 169 shm_last_free = segnum; 170 } 171 return 0; 172} 173 174struct shmdt_args { 175 void *shmaddr; 176}; 177int 178shmdt(p, uap, retval) 179 struct proc *p; 180 struct shmdt_args *uap; 181 int *retval; 182{ 183 struct shmmap_state *shmmap_s; 184 int i; 185 186 shmmap_s = (struct shmmap_state *)p->p_vmspace->vm_shm; 187 if (shmmap_s == NULL) 188 return EINVAL; 189 for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) 190 if (shmmap_s->shmid != -1 && 191 shmmap_s->va == (vm_offset_t)uap->shmaddr) 192 break; 193 if (i == shminfo.shmseg) 194 return EINVAL; 195 return shm_delete_mapping(p, shmmap_s); 196} 197 198struct shmat_args { 199 int shmid; 200 void *shmaddr; 201 int shmflg; 202}; 203int 204shmat(p, uap, retval) 205 struct proc *p; 206 struct shmat_args *uap; 207 int *retval; 208{ 209 int error, i, flags; 210 struct ucred *cred = p->p_ucred; 211 struct shmid_ds *shmseg; 212 struct shmmap_state *shmmap_s = NULL; 213 vm_offset_t attach_va; 214 vm_prot_t prot; 215 vm_size_t size; 216 217 shmmap_s = (struct shmmap_state *)p->p_vmspace->vm_shm; 218 if (shmmap_s == NULL) { 219 size = shminfo.shmseg * sizeof(struct shmmap_state); 220 shmmap_s = malloc(size, M_SHM, M_WAITOK); 221 for (i = 0; i < shminfo.shmseg; i++) 222 shmmap_s[i].shmid = -1; 223 p->p_vmspace->vm_shm = (caddr_t)shmmap_s; 224 } 225 shmseg = shm_find_segment_by_shmid(uap->shmid); 226 if (shmseg == NULL) 227 return EINVAL; 228 error = ipcperm(cred, &shmseg->shm_perm, 229 (uap->shmflg & SHM_RDONLY) ? IPC_R : IPC_R|IPC_W); 230 if (error) 231 return error; 232 for (i = 0; i < shminfo.shmseg; i++) { 233 if (shmmap_s->shmid == -1) 234 break; 235 shmmap_s++; 236 } 237 if (i >= shminfo.shmseg) 238 return EMFILE; 239 size = (shmseg->shm_segsz + CLOFSET) & ~CLOFSET; 240 prot = VM_PROT_READ; 241 if ((uap->shmflg & SHM_RDONLY) == 0) 242 prot |= VM_PROT_WRITE; 243 flags = MAP_ANON | MAP_SHARED; 244 if (uap->shmaddr) { 245 flags |= MAP_FIXED; 246 if (uap->shmflg & SHM_RND) 247 attach_va = (vm_offset_t)uap->shmaddr & ~(SHMLBA-1); 248 else if (((vm_offset_t)uap->shmaddr & (SHMLBA-1)) == 0) 249 attach_va = (vm_offset_t)uap->shmaddr; 250 else 251 return EINVAL; 252 } else { 253 /* This is just a hint to vm_mmap() about where to put it. */ 254 attach_va = round_page(p->p_vmspace->vm_taddr + MAXTSIZ + MAXDSIZ); 255 } 256 error = vm_mmap(&p->p_vmspace->vm_map, &attach_va, size, prot, 257 VM_PROT_DEFAULT, flags, (caddr_t) uap->shmid, 0); 258 if (error) 259 return error; 260 shmmap_s->va = attach_va; 261 shmmap_s->shmid = uap->shmid; 262 shmseg->shm_lpid = p->p_pid; 263 shmseg->shm_atime = time.tv_sec; 264 shmseg->shm_nattch++; 265 *retval = attach_va; 266 return 0; 267} 268 269struct oshmid_ds { 270 struct ipc_perm shm_perm; /* operation perms */ 271 int shm_segsz; /* size of segment (bytes) */ 272 ushort shm_cpid; /* pid, creator */ 273 ushort shm_lpid; /* pid, last operation */ 274 short shm_nattch; /* no. of current attaches */ 275 time_t shm_atime; /* last attach time */ 276 time_t shm_dtime; /* last detach time */ 277 time_t shm_ctime; /* last change time */ 278 void *shm_handle; /* internal handle for shm segment */ 279}; 280 281struct oshmctl_args { 282 int shmid; 283 int cmd; 284 struct oshmid_ds *ubuf; 285}; 286 287static int 288oshmctl(p, uap, retval) 289 struct proc *p; 290 struct oshmctl_args *uap; 291 int *retval; 292{ 293#ifdef COMPAT_43 294 int error; 295 struct ucred *cred = p->p_ucred; 296 struct shmid_ds *shmseg; 297 struct oshmid_ds outbuf; 298 299 shmseg = shm_find_segment_by_shmid(uap->shmid); 300 if (shmseg == NULL) 301 return EINVAL; 302 switch (uap->cmd) { 303 case IPC_STAT: 304 error = ipcperm(cred, &shmseg->shm_perm, IPC_R); 305 if (error) 306 return error; 307 outbuf.shm_perm = shmseg->shm_perm; 308 outbuf.shm_segsz = shmseg->shm_segsz; 309 outbuf.shm_cpid = shmseg->shm_cpid; 310 outbuf.shm_lpid = shmseg->shm_lpid; 311 outbuf.shm_nattch = shmseg->shm_nattch; 312 outbuf.shm_atime = shmseg->shm_atime; 313 outbuf.shm_dtime = shmseg->shm_dtime; 314 outbuf.shm_ctime = shmseg->shm_ctime; 315 outbuf.shm_handle = shmseg->shm_internal; 316 error = copyout((caddr_t)&outbuf, uap->ubuf, sizeof(outbuf)); 317 if (error) 318 return error; 319 break; 320 default: 321 /* XXX casting to (sy_call_t *) is bogus, as usual. */ 322 return ((sy_call_t *)shmctl)(p, uap, retval); 323 } 324 return 0; 325#else 326 return EINVAL; 327#endif 328} 329 330struct shmctl_args { 331 int shmid; 332 int cmd; 333 struct shmid_ds *ubuf; 334}; 335int 336shmctl(p, uap, retval) 337 struct proc *p; 338 struct shmctl_args *uap; 339 int *retval; 340{ 341 int error; 342 struct ucred *cred = p->p_ucred; 343 struct shmid_ds inbuf; 344 struct shmid_ds *shmseg; 345 346 shmseg = shm_find_segment_by_shmid(uap->shmid); 347 if (shmseg == NULL) 348 return EINVAL; 349 switch (uap->cmd) { 350 case IPC_STAT: 351 error = ipcperm(cred, &shmseg->shm_perm, IPC_R); 352 if (error) 353 return error; 354 error = copyout((caddr_t)shmseg, uap->ubuf, sizeof(inbuf)); 355 if (error) 356 return error; 357 break; 358 case IPC_SET: 359 error = ipcperm(cred, &shmseg->shm_perm, IPC_M); 360 if (error) 361 return error; 362 error = copyin(uap->ubuf, (caddr_t)&inbuf, sizeof(inbuf)); 363 if (error) 364 return error; 365 shmseg->shm_perm.uid = inbuf.shm_perm.uid; 366 shmseg->shm_perm.gid = inbuf.shm_perm.gid; 367 shmseg->shm_perm.mode = 368 (shmseg->shm_perm.mode & ~ACCESSPERMS) | 369 (inbuf.shm_perm.mode & ACCESSPERMS); 370 shmseg->shm_ctime = time.tv_sec; 371 break; 372 case IPC_RMID: 373 error = ipcperm(cred, &shmseg->shm_perm, IPC_M); 374 if (error) 375 return error; 376 shmseg->shm_perm.key = IPC_PRIVATE; 377 shmseg->shm_perm.mode |= SHMSEG_REMOVED; 378 if (shmseg->shm_nattch <= 0) { 379 shm_deallocate_segment(shmseg); 380 shm_last_free = IPCID_TO_IX(uap->shmid); 381 } 382 break; 383#if 0 384 case SHM_LOCK: 385 case SHM_UNLOCK: 386#endif 387 default: 388 return EINVAL; 389 } 390 return 0; 391} 392 393struct shmget_args { 394 key_t key; 395 size_t size; 396 int shmflg; 397}; 398static int 399shmget_existing(p, uap, mode, segnum, retval) 400 struct proc *p; 401 struct shmget_args *uap; 402 int mode; 403 int segnum; 404 int *retval; 405{ 406 struct shmid_ds *shmseg; 407 struct ucred *cred = p->p_ucred; 408 int error; 409 410 shmseg = &shmsegs[segnum]; 411 if (shmseg->shm_perm.mode & SHMSEG_REMOVED) { 412 /* 413 * This segment is in the process of being allocated. Wait 414 * until it's done, and look the key up again (in case the 415 * allocation failed or it was freed). 416 */ 417 shmseg->shm_perm.mode |= SHMSEG_WANTED; 418 error = tsleep((caddr_t)shmseg, PLOCK | PCATCH, "shmget", 0); 419 if (error) 420 return error; 421 return EAGAIN; 422 } 423 error = ipcperm(cred, &shmseg->shm_perm, mode); 424 if (error) 425 return error; 426 if (uap->size && uap->size > shmseg->shm_segsz) 427 return EINVAL; 428 if (uap->shmflg & (IPC_CREAT | IPC_EXCL) == (IPC_CREAT | IPC_EXCL)) 429 return EEXIST; 430 *retval = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm); 431 return 0; 432} 433 434static int 435shmget_allocate_segment(p, uap, mode, retval) 436 struct proc *p; 437 struct shmget_args *uap; 438 int mode; 439 int *retval; 440{ 441 int i, segnum, result, shmid, size; 442 struct ucred *cred = p->p_ucred; 443 struct shmid_ds *shmseg; 444 struct shm_handle *shm_handle; 445 446 if (uap->size < shminfo.shmmin || uap->size > shminfo.shmmax) 447 return EINVAL; 448 if (shm_nused >= shminfo.shmmni) /* any shmids left? */ 449 return ENOSPC; 450 size = (uap->size + CLOFSET) & ~CLOFSET; 451 if (shm_committed + btoc(size) > shminfo.shmall) 452 return ENOMEM; 453 if (shm_last_free < 0) { 454 for (i = 0; i < shminfo.shmmni; i++) 455 if (shmsegs[i].shm_perm.mode & SHMSEG_FREE) 456 break; 457 if (i == shminfo.shmmni) 458 panic("shmseg free count inconsistent"); 459 segnum = i; 460 } else { 461 segnum = shm_last_free; 462 shm_last_free = -1; 463 } 464 shmseg = &shmsegs[segnum]; 465 /* 466 * In case we sleep in malloc(), mark the segment present but deleted 467 * so that noone else tries to create the same key. 468 */ 469 shmseg->shm_perm.mode = SHMSEG_ALLOCATED | SHMSEG_REMOVED; 470 shmseg->shm_perm.key = uap->key; 471 shmseg->shm_perm.seq = (shmseg->shm_perm.seq + 1) & 0x7fff; 472 shm_handle = (struct shm_handle *) 473 malloc(sizeof(struct shm_handle), M_SHM, M_WAITOK); 474 shmid = IXSEQ_TO_IPCID(segnum, shmseg->shm_perm); 475 result = vm_mmap(sysvshm_map, &shm_handle->kva, size, VM_PROT_ALL, 476 VM_PROT_DEFAULT, MAP_ANON, (caddr_t) shmid, 0); 477 if (result != KERN_SUCCESS) { 478 shmseg->shm_perm.mode = SHMSEG_FREE; 479 shm_last_free = segnum; 480 free((caddr_t)shm_handle, M_SHM); 481 /* Just in case. */ 482 wakeup((caddr_t)shmseg); 483 return ENOMEM; 484 } 485 shmseg->shm_internal = shm_handle; 486 shmseg->shm_perm.cuid = shmseg->shm_perm.uid = cred->cr_uid; 487 shmseg->shm_perm.cgid = shmseg->shm_perm.gid = cred->cr_gid; 488 shmseg->shm_perm.mode = (shmseg->shm_perm.mode & SHMSEG_WANTED) | 489 (mode & ACCESSPERMS) | SHMSEG_ALLOCATED; 490 shmseg->shm_segsz = uap->size; 491 shmseg->shm_cpid = p->p_pid; 492 shmseg->shm_lpid = shmseg->shm_nattch = 0; 493 shmseg->shm_atime = shmseg->shm_dtime = 0; 494 shmseg->shm_ctime = time.tv_sec; 495 shm_committed += btoc(size); 496 shm_nused++; 497 if (shmseg->shm_perm.mode & SHMSEG_WANTED) { 498 /* 499 * Somebody else wanted this key while we were asleep. Wake 500 * them up now. 501 */ 502 shmseg->shm_perm.mode &= ~SHMSEG_WANTED; 503 wakeup((caddr_t)shmseg); 504 } 505 *retval = shmid; 506 return 0; 507} 508 509int 510shmget(p, uap, retval) 511 struct proc *p; 512 struct shmget_args *uap; 513 int *retval; 514{ 515 int segnum, mode, error; 516 517 mode = uap->shmflg & ACCESSPERMS; 518 if (uap->key != IPC_PRIVATE) { 519 again: 520 segnum = shm_find_segment_by_key(uap->key); 521 if (segnum >= 0) { 522 error = shmget_existing(p, uap, mode, segnum, retval); 523 if (error == EAGAIN) 524 goto again; 525 return error; 526 } 527 if ((uap->shmflg & IPC_CREAT) == 0) 528 return ENOENT; 529 } 530 return shmget_allocate_segment(p, uap, mode, retval); 531} 532 533int 534shmsys(p, uap, retval) 535 struct proc *p; 536 /* XXX actually varargs. */ 537 struct shmsys_args /* { 538 u_int which; 539 int a2; 540 int a3; 541 int a4; 542 } */ *uap; 543 int *retval; 544{ 545 546 if (uap->which >= sizeof(shmcalls)/sizeof(shmcalls[0])) 547 return EINVAL; 548 return ((*shmcalls[uap->which])(p, &uap->a2, retval)); 549} 550 551void 552shmfork(p1, p2, isvfork) 553 struct proc *p1, *p2; 554 int isvfork; 555{ 556 struct shmmap_state *shmmap_s; 557 size_t size; 558 int i; 559 560 size = shminfo.shmseg * sizeof(struct shmmap_state); 561 shmmap_s = malloc(size, M_SHM, M_WAITOK); 562 bcopy((caddr_t)p1->p_vmspace->vm_shm, (caddr_t)shmmap_s, size); 563 p2->p_vmspace->vm_shm = (caddr_t)shmmap_s; 564 for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) 565 if (shmmap_s->shmid != -1) 566 shmsegs[IPCID_TO_IX(shmmap_s->shmid)].shm_nattch++; 567} 568 569void 570shmexit(p) 571 struct proc *p; 572{ 573 struct shmmap_state *shmmap_s; 574 int i; 575 576 shmmap_s = (struct shmmap_state *)p->p_vmspace->vm_shm; 577 for (i = 0; i < shminfo.shmseg; i++, shmmap_s++) 578 if (shmmap_s->shmid != -1) 579 shm_delete_mapping(p, shmmap_s); 580 free((caddr_t)p->p_vmspace->vm_shm, M_SHM); 581 p->p_vmspace->vm_shm = NULL; 582} 583 584void 585shminit(dummy) 586 void *dummy; 587{ 588 int i; 589 vm_offset_t garbage1, garbage2; 590 591 /* actually this *should* be pageable. SHM_{LOCK,UNLOCK} */ 592 sysvshm_map = kmem_suballoc(kernel_map, &garbage1, &garbage2, 593 shminfo.shmall * NBPG, TRUE); 594 for (i = 0; i < shminfo.shmmni; i++) { 595 shmsegs[i].shm_perm.mode = SHMSEG_FREE; 596 shmsegs[i].shm_perm.seq = 0; 597 } 598 shm_last_free = 0; 599 shm_nused = 0; 600 shm_committed = 0; 601} 602