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

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