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

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