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. sys_shmat
  9. shm_open
  10. shm_close
  11. sys_shmdt
  12. shm_swap_in
  13. shm_swap

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

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