root/ipc/shm.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. shm_init
  2. findkey
  3. newseg
  4. sys_shmget
  5. killseg
  6. sys_shmctl
  7. shm_map
  8. add_vm_area
  9. sys_shmat
  10. detach
  11. sys_shmdt
  12. shm_exit
  13. shm_fork
  14. shm_swap_in
  15. shm_swap

   1 /*
   2  * linux/ipc/shm.c
   3  * Copyright (C) 1992, 1993 Krishna Balasubramanian
   4  *         Many improvements/fixes by Bruno Haible.
   5  */
   6 
   7 #include <linux/errno.h>
   8 #include <asm/segment.h>
   9 #include <linux/sched.h>
  10 #include <linux/ipc.h>
  11 #include <linux/shm.h>
  12 #include <linux/stat.h>
  13 #include <linux/malloc.h>
  14 
  15 extern int ipcperms (struct ipc_perm *ipcp, short shmflg);
  16 extern unsigned int get_swap_page (void);
  17 static int findkey (key_t key);
  18 static int newseg (key_t key, int shmflg, int size);
  19 static int shm_map (struct shm_desc *shmd, int remap);
  20 static void killseg (int id);
  21 static unsigned long shm_swap_in (struct vm_area_struct *, unsigned long);
  22 
  23 static int shm_tot = 0; /* total number of shared memory pages */
  24 static int shm_rss = 0; /* number of shared memory pages that are in memory */
  25 static int shm_swp = 0; /* number of shared memory pages that are in swap */
  26 static int max_shmid = 0; /* every used id is <= max_shmid */
  27 static struct wait_queue *shm_lock = NULL; /* calling findkey() may need to wait */
  28 static struct shmid_ds *shm_segs[SHMMNI];
  29 
  30 static unsigned short shm_seq = 0; /* incremented, for recognizing stale ids */
  31 
  32 /* some statistics */
  33 static ulong swap_attempts = 0;
  34 static ulong swap_successes = 0;
  35 static ulong used_segs = 0;
  36 
  37 void shm_init (void)
     /* [previous][next][first][last][top][bottom][index][help] */
  38 {
  39         int id;
  40 
  41         for (id = 0; id < SHMMNI; id++)
  42                 shm_segs[id] = (struct shmid_ds *) IPC_UNUSED;
  43         shm_tot = shm_rss = shm_seq = max_shmid = used_segs = 0;
  44         shm_lock = NULL;
  45         return;
  46 }
  47 
  48 static int findkey (key_t key)
     /* [previous][next][first][last][top][bottom][index][help] */
  49 {
  50         int id;
  51         struct shmid_ds *shp;
  52 
  53         for (id = 0; id <= max_shmid; id++) {
  54                 while ((shp = shm_segs[id]) == IPC_NOID)
  55                         sleep_on (&shm_lock);
  56                 if (shp == IPC_UNUSED)
  57                         continue;
  58                 if (key == shp->shm_perm.key)
  59                         return id;
  60         }
  61         return -1;
  62 }
  63 
  64 /*
  65  * allocate new shmid_ds and pgtable. protected by shm_segs[id] = NOID.
  66  */
  67 static int newseg (key_t key, int shmflg, int size)
     /* [previous][next][first][last][top][bottom][index][help] */
  68 {
  69         struct shmid_ds *shp;
  70         int numpages = (size + PAGE_SIZE -1) >> PAGE_SHIFT;
  71         int id, i;
  72 
  73         if (size < SHMMIN)
  74                 return -EINVAL;
  75         if (shm_tot + numpages >= SHMALL)
  76                 return -ENOSPC;
  77         for (id = 0; id < SHMMNI; id++)
  78                 if (shm_segs[id] == IPC_UNUSED) {
  79                         shm_segs[id] = (struct shmid_ds *) IPC_NOID;
  80                         goto found;
  81                 }
  82         return -ENOSPC;
  83 
  84 found:
  85         shp = (struct shmid_ds *) kmalloc (sizeof (*shp), GFP_KERNEL);
  86         if (!shp) {
  87                 shm_segs[id] = (struct shmid_ds *) IPC_UNUSED;
  88                 if (shm_lock)
  89                         wake_up (&shm_lock);
  90                 return -ENOMEM;
  91         }
  92 
  93         shp->shm_pages = (ulong *) kmalloc (numpages*sizeof(ulong),GFP_KERNEL);
  94         if (!shp->shm_pages) {
  95                 shm_segs[id] = (struct shmid_ds *) IPC_UNUSED;
  96                 if (shm_lock)
  97                         wake_up (&shm_lock);
  98                 kfree(shp);
  99                 return -ENOMEM;
 100         }
 101 
 102         for (i = 0; i < numpages; shp->shm_pages[i++] = 0);
 103         shm_tot += numpages;
 104         shp->shm_perm.key = key;
 105         shp->shm_perm.mode = (shmflg & S_IRWXUGO);
 106         shp->shm_perm.cuid = shp->shm_perm.uid = current->euid;
 107         shp->shm_perm.cgid = shp->shm_perm.gid = current->egid;
 108         shp->shm_perm.seq = shm_seq;
 109         shp->shm_segsz = size;
 110         shp->shm_cpid = current->pid;
 111         shp->attaches = NULL;
 112         shp->shm_lpid = shp->shm_nattch = 0;
 113         shp->shm_atime = shp->shm_dtime = 0;
 114         shp->shm_ctime = CURRENT_TIME;
 115         shp->shm_npages = numpages;
 116 
 117         if (id > max_shmid)
 118                 max_shmid = id;
 119         shm_segs[id] = shp;
 120         used_segs++;
 121         if (shm_lock)
 122                 wake_up (&shm_lock);
 123         return (unsigned int) shp->shm_perm.seq * SHMMNI + id;
 124 }
 125 
 126 int sys_shmget (key_t key, int size, int shmflg)
     /* [previous][next][first][last][top][bottom][index][help] */
 127 {
 128         struct shmid_ds *shp;
 129         int id = 0;
 130 
 131         if (size < 0 || size > SHMMAX)
 132                 return -EINVAL;
 133         if (key == IPC_PRIVATE)
 134                 return newseg(key, shmflg, size);
 135         if ((id = findkey (key)) == -1) {
 136                 if (!(shmflg & IPC_CREAT))
 137                         return -ENOENT;
 138                 return newseg(key, shmflg, size);
 139         }
 140         if ((shmflg & IPC_CREAT) && (shmflg & IPC_EXCL))
 141                 return -EEXIST;
 142         shp = shm_segs[id];
 143         if (shp->shm_perm.mode & SHM_DEST)
 144                 return -EIDRM;
 145         if (size > shp->shm_segsz)
 146                 return -EINVAL;
 147         if (ipcperms (&shp->shm_perm, shmflg))
 148                 return -EACCES;
 149         return (unsigned int) shp->shm_perm.seq * SHMMNI + id;
 150 }
 151 
 152 /*
 153  * Only called after testing nattch and SHM_DEST.
 154  * Here pages, pgtable and shmid_ds are freed.
 155  */
 156 static void killseg (int id)
     /* [previous][next][first][last][top][bottom][index][help] */
 157 {
 158         struct shmid_ds *shp;
 159         int i, numpages;
 160         ulong page;
 161 
 162         shp = shm_segs[id];
 163         if (shp == IPC_NOID || shp == IPC_UNUSED) {
 164                 printk ("shm nono: killseg called on unused seg id=%d\n", id);
 165                 return;
 166         }
 167         shp->shm_perm.seq++;     /* for shmat */
 168         shm_seq = (shm_seq+1) % ((unsigned)(1<<31)/SHMMNI); /* increment, but avoid overflow */
 169         shm_segs[id] = (struct shmid_ds *) IPC_UNUSED;
 170         used_segs--;
 171         if (id == max_shmid)
 172                 while (max_shmid && (shm_segs[--max_shmid] == IPC_UNUSED));
 173         if (!shp->shm_pages) {
 174                 printk ("shm nono: killseg shp->pages=NULL. id=%d\n", id);
 175                 return;
 176         }
 177         numpages = shp->shm_npages;
 178         for (i = 0; i < numpages ; i++) {
 179                 if (!(page = shp->shm_pages[i]))
 180                         continue;
 181                 if (page & PAGE_PRESENT) {
 182                         free_page (page & PAGE_MASK);
 183                         shm_rss--;
 184                 } else {
 185                         swap_free (page);
 186                         shm_swp--;
 187                 }
 188         }
 189         kfree(shp->shm_pages);
 190         shm_tot -= numpages;
 191         kfree(shp);
 192         return;
 193 }
 194 
 195 int sys_shmctl (int shmid, int cmd, struct shmid_ds *buf)
     /* [previous][next][first][last][top][bottom][index][help] */
 196 {
 197         struct shmid_ds tbuf;
 198         struct shmid_ds *shp;
 199         struct ipc_perm *ipcp;
 200         int id, err;
 201 
 202         if (cmd < 0 || shmid < 0)
 203                 return -EINVAL;
 204         if (cmd == IPC_SET) {
 205                 if (!buf)
 206                         return -EFAULT;
 207                 err = verify_area (VERIFY_READ, buf, sizeof (*buf));
 208                 if (err)
 209                         return err;
 210                 memcpy_fromfs (&tbuf, buf, sizeof (*buf));
 211         }
 212 
 213         switch (cmd) { /* replace with proc interface ? */
 214         case IPC_INFO:
 215         {
 216                 struct shminfo shminfo;
 217                 if (!buf)
 218                         return -EFAULT;
 219                 shminfo.shmmni = SHMMNI;
 220                 shminfo.shmmax = SHMMAX;
 221                 shminfo.shmmin = SHMMIN;
 222                 shminfo.shmall = SHMALL;
 223                 shminfo.shmseg = SHMSEG;
 224                 err = verify_area (VERIFY_WRITE, buf, sizeof (struct shminfo));
 225                 if (err)
 226                         return err;
 227                 memcpy_tofs (buf, &shminfo, sizeof(struct shminfo));
 228                 return max_shmid;
 229         }
 230         case SHM_INFO:
 231         {
 232                 struct shm_info shm_info;
 233                 if (!buf)
 234                         return -EFAULT;
 235                 err = verify_area (VERIFY_WRITE, buf, sizeof (shm_info));
 236                 if (err)
 237                         return err;
 238                 shm_info.used_ids = used_segs;
 239                 shm_info.shm_rss = shm_rss;
 240                 shm_info.shm_tot = shm_tot;
 241                 shm_info.shm_swp = shm_swp;
 242                 shm_info.swap_attempts = swap_attempts;
 243                 shm_info.swap_successes = swap_successes;
 244                 memcpy_tofs (buf, &shm_info, sizeof(shm_info));
 245                 return max_shmid;
 246         }
 247         case SHM_STAT:
 248                 if (!buf)
 249                         return -EFAULT;
 250                 err = verify_area (VERIFY_WRITE, buf, sizeof (*buf));
 251                 if (err)
 252                         return err;
 253                 if (shmid > max_shmid)
 254                         return -EINVAL;
 255                 shp = shm_segs[shmid];
 256                 if (shp == IPC_UNUSED || shp == IPC_NOID)
 257                         return -EINVAL;
 258                 if (ipcperms (&shp->shm_perm, S_IRUGO))
 259                         return -EACCES;
 260                 id = (unsigned int) shp->shm_perm.seq * SHMMNI + shmid;
 261                 tbuf.shm_perm   = shp->shm_perm;
 262                 tbuf.shm_segsz  = shp->shm_segsz;
 263                 tbuf.shm_atime  = shp->shm_atime;
 264                 tbuf.shm_dtime  = shp->shm_dtime;
 265                 tbuf.shm_ctime  = shp->shm_ctime;
 266                 tbuf.shm_cpid   = shp->shm_cpid;
 267                 tbuf.shm_lpid   = shp->shm_lpid;
 268                 tbuf.shm_nattch = shp->shm_nattch;
 269                 memcpy_tofs (buf, &tbuf, sizeof(*buf));
 270                 return id;
 271         }
 272 
 273         shp = shm_segs[id = (unsigned int) shmid % SHMMNI];
 274         if (shp == IPC_UNUSED || shp == IPC_NOID)
 275                 return -EINVAL;
 276         if (shp->shm_perm.seq != (unsigned int) shmid / SHMMNI)
 277                 return -EIDRM;
 278         ipcp = &shp->shm_perm;
 279 
 280         switch (cmd) {
 281         case SHM_UNLOCK:
 282                 if (!suser())
 283                         return -EPERM;
 284                 if (!(ipcp->mode & SHM_LOCKED))
 285                         return -EINVAL;
 286                 ipcp->mode &= ~SHM_LOCKED;
 287                 break;
 288         case SHM_LOCK:
 289 /* Allow superuser to lock segment in memory */
 290 /* Should the pages be faulted in here or leave it to user? */
 291 /* need to determine interaction with current->swappable */
 292                 if (!suser())
 293                         return -EPERM;
 294                 if (ipcp->mode & SHM_LOCKED)
 295                         return -EINVAL;
 296                 ipcp->mode |= SHM_LOCKED;
 297                 break;
 298         case IPC_STAT:
 299                 if (ipcperms (ipcp, S_IRUGO))
 300                         return -EACCES;
 301                 if (!buf)
 302                         return -EFAULT;
 303                 err = verify_area (VERIFY_WRITE, buf, sizeof (*buf));
 304                 if (err)
 305                         return err;
 306                 tbuf.shm_perm   = shp->shm_perm;
 307                 tbuf.shm_segsz  = shp->shm_segsz;
 308                 tbuf.shm_atime  = shp->shm_atime;
 309                 tbuf.shm_dtime  = shp->shm_dtime;
 310                 tbuf.shm_ctime  = shp->shm_ctime;
 311                 tbuf.shm_cpid   = shp->shm_cpid;
 312                 tbuf.shm_lpid   = shp->shm_lpid;
 313                 tbuf.shm_nattch = shp->shm_nattch;
 314                 memcpy_tofs (buf, &tbuf, sizeof(*buf));
 315                 break;
 316         case IPC_SET:
 317                 if (suser() || current->euid == shp->shm_perm.uid ||
 318                     current->euid == shp->shm_perm.cuid) {
 319                         ipcp->uid = tbuf.shm_perm.uid;
 320                         ipcp->gid = tbuf.shm_perm.gid;
 321                         ipcp->mode = (ipcp->mode & ~S_IRWXUGO)
 322                                 | (tbuf.shm_perm.mode & S_IRWXUGO);
 323                         shp->shm_ctime = CURRENT_TIME;
 324                         break;
 325                 }
 326                 return -EPERM;
 327         case IPC_RMID:
 328                 if (suser() || current->euid == shp->shm_perm.uid ||
 329                     current->euid == shp->shm_perm.cuid) {
 330                         shp->shm_perm.mode |= SHM_DEST;
 331                         if (shp->shm_nattch <= 0)
 332                                 killseg (id);
 333                         break;
 334                 }
 335                 return -EPERM;
 336         default:
 337                 return -EINVAL;
 338         }
 339         return 0;
 340 }
 341 
 342 /*
 343  * check range is unmapped, ensure page tables exist
 344  * mark page table entries with shm_sgn.
 345  * if remap != 0 the range is remapped.
 346  */
 347 static int shm_map (struct shm_desc *shmd, int remap)
     /* [previous][next][first][last][top][bottom][index][help] */
 348 {
 349         unsigned long invalid = 0;
 350         unsigned long *page_table;
 351         unsigned long tmp, shm_sgn;
 352         unsigned long page_dir = shmd->task->tss.cr3;
 353 
 354         /* check that the range is unmapped and has page_tables */
 355         for (tmp = shmd->start; tmp < shmd->end; tmp += PAGE_SIZE) {
 356                 page_table = PAGE_DIR_OFFSET(page_dir,tmp);
 357                 if (*page_table & PAGE_PRESENT) {
 358                         page_table = (ulong *) (PAGE_MASK & *page_table);
 359                         page_table += ((tmp >> PAGE_SHIFT) & (PTRS_PER_PAGE-1));
 360                         if (*page_table) {
 361                                 if (!remap)
 362                                         return -EINVAL;
 363                                 if (*page_table & PAGE_PRESENT) {
 364                                         --current->mm->rss;
 365                                         free_page (*page_table & PAGE_MASK);
 366                                 }
 367                                 else
 368                                         swap_free (*page_table);
 369                                 invalid++;
 370                         }
 371                         continue;
 372                 }
 373               {
 374                 unsigned long new_pt;
 375                 if (!(new_pt = get_free_page(GFP_KERNEL)))
 376                         return -ENOMEM;
 377                 *page_table = new_pt | PAGE_TABLE;
 378                 tmp |= ((PAGE_SIZE << 10) - PAGE_SIZE);
 379         }}
 380         if (invalid)
 381                 invalidate();
 382 
 383         /* map page range */
 384         shm_sgn = shmd->shm_sgn;
 385         for (tmp = shmd->start; tmp < shmd->end; tmp += PAGE_SIZE,
 386              shm_sgn += (1 << SHM_IDX_SHIFT)) {
 387                 page_table = PAGE_DIR_OFFSET(page_dir,tmp);
 388                 page_table = (ulong *) (PAGE_MASK & *page_table);
 389                 page_table += (tmp >> PAGE_SHIFT) & (PTRS_PER_PAGE-1);
 390                 *page_table = shm_sgn;
 391         }
 392         return 0;
 393 }
 394 
 395 static struct vm_operations_struct shm_vm_ops = {
 396         NULL,                   /* open */
 397         NULL,                   /* close */
 398         NULL,                   /* nopage (done with swapin) */
 399         NULL,                   /* wppage */
 400         NULL,                   /* share */
 401         NULL,                   /* unmap */
 402         NULL,                   /* swapout (hardcoded right now) */
 403         shm_swap_in             /* swapin */
 404 };
 405 
 406 /*
 407  * This is really minimal support to make the shared mem stuff
 408  * be known by the general VM manager. It should add the vm_ops
 409  * field so that 'munmap()' and friends work correctly on shared
 410  * memory areas..
 411  */
 412 static int add_vm_area(unsigned long addr, unsigned long len, int readonly)
     /* [previous][next][first][last][top][bottom][index][help] */
 413 {
 414         struct vm_area_struct * vma;
 415 
 416         vma = (struct vm_area_struct * ) kmalloc(sizeof(struct vm_area_struct), GFP_KERNEL);
 417         if (!vma)
 418                 return -ENOMEM;
 419         do_munmap(addr, len);
 420         vma->vm_task = current;
 421         vma->vm_start = addr;
 422         vma->vm_end = addr + len;
 423         vma->vm_flags = VM_SHM | VM_SHARED | VM_MAYREAD | VM_MAYEXEC | VM_READ | VM_EXEC;
 424         if (readonly)
 425                 vma->vm_page_prot = PAGE_READONLY;
 426         else {
 427                 vma->vm_flags |= VM_MAYWRITE | VM_WRITE;
 428                 vma->vm_page_prot = PAGE_SHARED;
 429         }
 430         vma->vm_share = NULL;
 431         vma->vm_inode = NULL;
 432         vma->vm_offset = 0;
 433         vma->vm_ops = &shm_vm_ops;
 434         insert_vm_struct(current, vma);
 435         merge_segments(current->mm->mmap);
 436         return 0;
 437 }
 438 
 439 /*
 440  * Fix shmaddr, allocate descriptor, map shm, add attach descriptor to lists.
 441  * raddr is needed to return addresses above 2Gig.
 442  */
 443 int sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *raddr)
     /* [previous][next][first][last][top][bottom][index][help] */
 444 {
 445         struct shmid_ds *shp;
 446         struct shm_desc *shmd;
 447         int err;
 448         unsigned int id;
 449         unsigned long addr;
 450 
 451         if (shmid < 0)
 452                 return -EINVAL;
 453 
 454         if (raddr) {
 455                 err = verify_area(VERIFY_WRITE, raddr, sizeof(ulong));
 456                 if (err)
 457                         return err;
 458         }
 459 
 460         shp = shm_segs[id = (unsigned int) shmid % SHMMNI];
 461         if (shp == IPC_UNUSED || shp == IPC_NOID)
 462                 return -EINVAL;
 463 
 464         if (!(addr = (ulong) shmaddr)) {
 465                 if (shmflg & SHM_REMAP)
 466                         return -EINVAL;
 467                 /* set addr below  all current unspecified attaches */
 468                 addr = SHM_RANGE_END;
 469                 for (shmd = current->shm; shmd; shmd = shmd->task_next) {
 470                         if (shmd->start < SHM_RANGE_START)
 471                                 continue;
 472                         if (addr >= shmd->start)
 473                                 addr = shmd->start;
 474                 }
 475                 addr = (addr - shp->shm_segsz) & PAGE_MASK;
 476         } else if (addr & (SHMLBA-1)) {
 477                 if (shmflg & SHM_RND)
 478                         addr &= ~(SHMLBA-1);       /* round down */
 479                 else
 480                         return -EINVAL;
 481         }
 482         if ((addr > current->mm->start_stack - 16384 - PAGE_SIZE*shp->shm_npages))
 483                 return -EINVAL;
 484         if (shmflg & SHM_REMAP)
 485                 for (shmd = current->shm; shmd; shmd = shmd->task_next) {
 486                         if (addr >= shmd->start && addr < shmd->end)
 487                                 return -EINVAL;
 488                         if (addr + shp->shm_segsz >= shmd->start &&
 489                             addr + shp->shm_segsz < shmd->end)
 490                                 return -EINVAL;
 491                 }
 492 
 493         if (ipcperms(&shp->shm_perm, shmflg & SHM_RDONLY ? S_IRUGO : S_IRUGO|S_IWUGO))
 494                 return -EACCES;
 495         if (shp->shm_perm.seq != (unsigned int) shmid / SHMMNI)
 496                 return -EIDRM;
 497 
 498         shmd = (struct shm_desc *) kmalloc (sizeof(*shmd), GFP_KERNEL);
 499         if (!shmd)
 500                 return -ENOMEM;
 501         if ((shp != shm_segs[id]) || (shp->shm_perm.seq != (unsigned int) shmid / SHMMNI)) {
 502                 kfree(shmd);
 503                 return -EIDRM;
 504         }
 505         shmd->shm_sgn = (SHM_SWP_TYPE << 1) | (id << SHM_ID_SHIFT) |
 506                 (shmflg & SHM_RDONLY ? SHM_READ_ONLY : 0);
 507         shmd->start = addr;
 508         shmd->end = addr + shp->shm_npages * PAGE_SIZE;
 509         shmd->task = current;
 510 
 511         if ((err = add_vm_area(shmd->start, shmd->end - shmd->start, shmflg & SHM_RDONLY))) {
 512                 kfree(shmd);
 513                 return err;
 514         }
 515 
 516         shp->shm_nattch++;            /* prevent destruction */
 517 
 518         if ((err = shm_map (shmd, shmflg & SHM_REMAP))) {
 519                 if (--shp->shm_nattch <= 0 && shp->shm_perm.mode & SHM_DEST)
 520                         killseg(id);
 521                 kfree(shmd);
 522                 return err;
 523         }
 524 
 525         shmd->task_next = current->shm;
 526         current->shm = shmd;
 527         shmd->seg_next = shp->attaches;
 528         shp->attaches = shmd;
 529         shp->shm_lpid = current->pid;
 530         shp->shm_atime = CURRENT_TIME;
 531         if (!raddr)
 532                 return addr;
 533         put_fs_long (addr, raddr);
 534         return 0;
 535 }
 536 
 537 /*
 538  * remove the first attach descriptor from the list *shmdp.
 539  * free memory for segment if it is marked destroyed.
 540  * The descriptor is detached before the sleep in unmap_page_range.
 541  */
 542 static void detach (struct shm_desc **shmdp)
     /* [previous][next][first][last][top][bottom][index][help] */
 543 {
 544         struct shm_desc *shmd = *shmdp;
 545         struct shmid_ds *shp;
 546         int id;
 547 
 548         id = (shmd->shm_sgn >> SHM_ID_SHIFT) & SHM_ID_MASK;
 549         shp = shm_segs[id];
 550         *shmdp = shmd->task_next;
 551         for (shmdp = &shp->attaches; *shmdp; shmdp = &(*shmdp)->seg_next)
 552                 if (*shmdp == shmd) {
 553                         *shmdp = shmd->seg_next;
 554                         goto found;
 555                 }
 556         printk("detach: shm segment (id=%d) attach list inconsistent\n",id);
 557 
 558  found:
 559         do_munmap(shmd->start, shp->shm_segsz);
 560         kfree(shmd);
 561         shp->shm_lpid = current->pid;
 562         shp->shm_dtime = CURRENT_TIME;
 563         if (--shp->shm_nattch <= 0 && shp->shm_perm.mode & SHM_DEST)
 564                 killseg (id); /* sleeps */
 565         return;
 566 }
 567 
 568 /*
 569  * detach and kill segment if marked destroyed.
 570  * The work is done in detach.
 571  */
 572 int sys_shmdt (char *shmaddr)
     /* [previous][next][first][last][top][bottom][index][help] */
 573 {
 574         struct shm_desc *shmd, **shmdp;
 575 
 576         for (shmdp = &current->shm; (shmd = *shmdp); shmdp=&shmd->task_next) {
 577                 if (shmd->start == (ulong) shmaddr) {
 578                         detach (shmdp);
 579                         return 0;
 580                 }
 581         }
 582         return -EINVAL;
 583 }
 584 
 585 /*
 586  * detach all attached segments.
 587  */
 588 void shm_exit (void)
     /* [previous][next][first][last][top][bottom][index][help] */
 589 {
 590         while (current->shm)
 591                 detach(&current->shm);
 592         return;
 593 }
 594 
 595 /*
 596  * copy the parent shm descriptors and update nattch
 597  * parent is stuck in fork so an attach on each segment is assured.
 598  * copy_page_tables does the mapping.
 599  */
 600 int shm_fork (struct task_struct *p1, struct task_struct *p2)
     /* [previous][next][first][last][top][bottom][index][help] */
 601 {
 602         struct shm_desc *shmd, *new_desc = NULL, *tmp;
 603         struct shmid_ds *shp;
 604         int id;
 605 
 606         p2->semundo = NULL;
 607         p2->shm = NULL;
 608         if (!p1->shm)
 609                 return 0;
 610         for (shmd = p1->shm; shmd; shmd = shmd->task_next) {
 611                 tmp = (struct shm_desc *) kmalloc(sizeof(*tmp), GFP_KERNEL);
 612                 if (!tmp) {
 613                         while (new_desc) {
 614                                 tmp = new_desc->task_next;
 615                                 kfree(new_desc);
 616                                 new_desc = tmp;
 617                         }
 618                         free_page_tables (p2);
 619                         return -ENOMEM;
 620                 }
 621                 *tmp = *shmd;
 622                 tmp->task = p2;
 623                 tmp->task_next = new_desc;
 624                 new_desc = tmp;
 625         }
 626         p2->shm = new_desc;
 627         for (shmd = new_desc; shmd; shmd = shmd->task_next) {
 628                 id = (shmd->shm_sgn >> SHM_ID_SHIFT) & SHM_ID_MASK;
 629                 shp = shm_segs[id];
 630                 if (shp == IPC_UNUSED) {
 631                         printk("shm_fork: unused id=%d PANIC\n", id);
 632                         return -ENOMEM;
 633                 }
 634                 shmd->seg_next = shp->attaches;
 635                 shp->attaches = shmd;
 636                 shp->shm_nattch++;
 637                 shp->shm_atime = CURRENT_TIME;
 638                 shp->shm_lpid = current->pid;
 639         }
 640         return 0;
 641 }
 642 
 643 /*
 644  * page not present ... go through shm_pages
 645  */
 646 static unsigned long shm_swap_in(struct vm_area_struct * vma, unsigned long code)
     /* [previous][next][first][last][top][bottom][index][help] */
 647 {
 648         unsigned long page;
 649         struct shmid_ds *shp;
 650         unsigned int id, idx;
 651 
 652         id = (code >> SHM_ID_SHIFT) & SHM_ID_MASK;
 653         if (id > max_shmid) {
 654                 printk ("shm_no_page: id=%d too big. proc mem corrupted\n", id);
 655                 return BAD_PAGE | PAGE_SHARED;
 656         }
 657         shp = shm_segs[id];
 658         if (shp == IPC_UNUSED || shp == IPC_NOID) {
 659                 printk ("shm_no_page: id=%d invalid. Race.\n", id);
 660                 return BAD_PAGE | PAGE_SHARED;
 661         }
 662         idx = (code >> SHM_IDX_SHIFT) & SHM_IDX_MASK;
 663         if (idx >= shp->shm_npages) {
 664                 printk ("shm_no_page : too large page index. id=%d\n", id);
 665                 return BAD_PAGE | PAGE_SHARED;
 666         }
 667 
 668         if (!(shp->shm_pages[idx] & PAGE_PRESENT)) {
 669                 if(!(page = get_free_page(GFP_KERNEL))) {
 670                         oom(current);
 671                         return BAD_PAGE | PAGE_SHARED;
 672                 }
 673                 if (shp->shm_pages[idx] & PAGE_PRESENT) {
 674                         free_page (page);
 675                         goto done;
 676                 }
 677                 if (shp->shm_pages[idx]) {
 678                         read_swap_page (shp->shm_pages[idx], (char *) page);
 679                         if (shp->shm_pages[idx] & PAGE_PRESENT)  {
 680                                 free_page (page);
 681                                 goto done;
 682                         }
 683                         swap_free (shp->shm_pages[idx]);
 684                         shm_swp--;
 685                 }
 686                 shm_rss++;
 687                 shp->shm_pages[idx] = page | (PAGE_SHARED | PAGE_DIRTY);
 688         } else
 689                 --current->mm->maj_flt;  /* was incremented in do_no_page */
 690 
 691 done:
 692         current->mm->min_flt++;
 693         page = shp->shm_pages[idx];
 694         if (code & SHM_READ_ONLY)           /* write-protect */
 695                 page &= ~PAGE_RW;
 696         mem_map[MAP_NR(page)]++;
 697         return page;
 698 }
 699 
 700 /*
 701  * Goes through counter = (shm_rss << prio) present shm pages.
 702  */
 703 static unsigned long swap_id = 0; /* currently being swapped */
 704 static unsigned long swap_idx = 0; /* next to swap */
 705 
 706 int shm_swap (int prio)
     /* [previous][next][first][last][top][bottom][index][help] */
 707 {
 708         unsigned long page;
 709         struct shmid_ds *shp;
 710         struct shm_desc *shmd;
 711         unsigned int swap_nr;
 712         unsigned long id, idx, invalid = 0;
 713         int counter;
 714 
 715         counter = shm_rss >> prio;
 716         if (!counter || !(swap_nr = get_swap_page()))
 717                 return 0;
 718 
 719  check_id:
 720         shp = shm_segs[swap_id];
 721         if (shp == IPC_UNUSED || shp == IPC_NOID || shp->shm_perm.mode & SHM_LOCKED ) {
 722                 swap_idx = 0;
 723                 if (++swap_id > max_shmid)
 724                         swap_id = 0;
 725                 goto check_id;
 726         }
 727         id = swap_id;
 728 
 729  check_table:
 730         idx = swap_idx++;
 731         if (idx >= shp->shm_npages) {
 732                 swap_idx = 0;
 733                 if (++swap_id > max_shmid)
 734                         swap_id = 0;
 735                 goto check_id;
 736         }
 737 
 738         page = shp->shm_pages[idx];
 739         if (!(page & PAGE_PRESENT))
 740                 goto check_table;
 741         swap_attempts++;
 742 
 743         if (--counter < 0) { /* failed */
 744                 if (invalid)
 745                         invalidate();
 746                 swap_free (swap_nr);
 747                 return 0;
 748         }
 749         for (shmd = shp->attaches; shmd; shmd = shmd->seg_next) {
 750                 unsigned long tmp, *pte;
 751                 if ((shmd->shm_sgn >> SHM_ID_SHIFT & SHM_ID_MASK) != id) {
 752                         printk ("shm_swap: id=%ld does not match shmd\n", id);
 753                         continue;
 754                 }
 755                 tmp = shmd->start + (idx << PAGE_SHIFT);
 756                 if (tmp >= shmd->end) {
 757                         printk ("shm_swap: too large idx=%ld id=%ld PANIC\n",idx, id);
 758                         continue;
 759                 }
 760                 pte = PAGE_DIR_OFFSET(shmd->task->tss.cr3,tmp);
 761                 if (!(*pte & PAGE_PRESENT)) {
 762                         printk("shm_swap: bad pgtbl! id=%ld start=%lx idx=%ld\n",
 763                                         id, shmd->start, idx);
 764                         *pte = 0;
 765                         continue;
 766                 }
 767                 pte = (ulong *) (PAGE_MASK & *pte);
 768                 pte += ((tmp >> PAGE_SHIFT) & (PTRS_PER_PAGE-1));
 769                 tmp = *pte;
 770                 if (!(tmp & PAGE_PRESENT))
 771                         continue;
 772                 if (tmp & PAGE_ACCESSED) {
 773                         *pte &= ~PAGE_ACCESSED;
 774                         continue;
 775                 }
 776                 tmp = shmd->shm_sgn | idx << SHM_IDX_SHIFT;
 777                 *pte = tmp;
 778                 mem_map[MAP_NR(page)]--;
 779                 shmd->task->mm->rss--;
 780                 invalid++;
 781         }
 782 
 783         if (mem_map[MAP_NR(page)] != 1)
 784                 goto check_table;
 785         page &= PAGE_MASK;
 786         shp->shm_pages[idx] = swap_nr;
 787         if (invalid)
 788                 invalidate();
 789         write_swap_page (swap_nr, (char *) page);
 790         free_page (page);
 791         swap_successes++;
 792         shm_swp++;
 793         shm_rss--;
 794         return 1;
 795 }

/* [previous][next][first][last][top][bottom][index][help] */