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 <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 pte_t shm_swap_in(struct vm_area_struct *, unsigned long, 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 
 165         shp = shm_segs[id];
 166         if (shp == IPC_NOID || shp == IPC_UNUSED) {
 167                 printk ("shm nono: killseg called on unused seg id=%d\n", id);
 168                 return;
 169         }
 170         shp->shm_perm.seq++;     /* for shmat */
 171         shm_seq = (shm_seq+1) % ((unsigned)(1<<31)/SHMMNI); /* increment, but avoid overflow */
 172         shm_segs[id] = (struct shmid_ds *) IPC_UNUSED;
 173         used_segs--;
 174         if (id == max_shmid)
 175                 while (max_shmid && (shm_segs[--max_shmid] == IPC_UNUSED));
 176         if (!shp->shm_pages) {
 177                 printk ("shm nono: killseg shp->pages=NULL. id=%d\n", id);
 178                 return;
 179         }
 180         numpages = shp->shm_npages;
 181         for (i = 0; i < numpages ; i++) {
 182                 pte_t pte;
 183                 pte_val(pte) = shp->shm_pages[i];
 184                 if (pte_none(pte))
 185                         continue;
 186                 if (pte_present(pte)) {
 187                         free_page (pte_page(pte));
 188                         shm_rss--;
 189                 } else {
 190                         swap_free(pte_val(pte));
 191                         shm_swp--;
 192                 }
 193         }
 194         kfree(shp->shm_pages);
 195         shm_tot -= numpages;
 196         kfree(shp);
 197         return;
 198 }
 199 
 200 int sys_shmctl (int shmid, int cmd, struct shmid_ds *buf)
     /* [previous][next][first][last][top][bottom][index][help] */
 201 {
 202         struct shmid_ds tbuf;
 203         struct shmid_ds *shp;
 204         struct ipc_perm *ipcp;
 205         int id, err;
 206 
 207         if (cmd < 0 || shmid < 0)
 208                 return -EINVAL;
 209         if (cmd == IPC_SET) {
 210                 if (!buf)
 211                         return -EFAULT;
 212                 err = verify_area (VERIFY_READ, buf, sizeof (*buf));
 213                 if (err)
 214                         return err;
 215                 memcpy_fromfs (&tbuf, buf, sizeof (*buf));
 216         }
 217 
 218         switch (cmd) { /* replace with proc interface ? */
 219         case IPC_INFO:
 220         {
 221                 struct shminfo shminfo;
 222                 if (!buf)
 223                         return -EFAULT;
 224                 shminfo.shmmni = SHMMNI;
 225                 shminfo.shmmax = SHMMAX;
 226                 shminfo.shmmin = SHMMIN;
 227                 shminfo.shmall = SHMALL;
 228                 shminfo.shmseg = SHMSEG;
 229                 err = verify_area (VERIFY_WRITE, buf, sizeof (struct shminfo));
 230                 if (err)
 231                         return err;
 232                 memcpy_tofs (buf, &shminfo, sizeof(struct shminfo));
 233                 return max_shmid;
 234         }
 235         case SHM_INFO:
 236         {
 237                 struct shm_info shm_info;
 238                 if (!buf)
 239                         return -EFAULT;
 240                 err = verify_area (VERIFY_WRITE, buf, sizeof (shm_info));
 241                 if (err)
 242                         return err;
 243                 shm_info.used_ids = used_segs;
 244                 shm_info.shm_rss = shm_rss;
 245                 shm_info.shm_tot = shm_tot;
 246                 shm_info.shm_swp = shm_swp;
 247                 shm_info.swap_attempts = swap_attempts;
 248                 shm_info.swap_successes = swap_successes;
 249                 memcpy_tofs (buf, &shm_info, sizeof(shm_info));
 250                 return max_shmid;
 251         }
 252         case SHM_STAT:
 253                 if (!buf)
 254                         return -EFAULT;
 255                 err = verify_area (VERIFY_WRITE, buf, sizeof (*buf));
 256                 if (err)
 257                         return err;
 258                 if (shmid > max_shmid)
 259                         return -EINVAL;
 260                 shp = shm_segs[shmid];
 261                 if (shp == IPC_UNUSED || shp == IPC_NOID)
 262                         return -EINVAL;
 263                 if (ipcperms (&shp->shm_perm, S_IRUGO))
 264                         return -EACCES;
 265                 id = (unsigned int) shp->shm_perm.seq * SHMMNI + shmid;
 266                 tbuf.shm_perm   = shp->shm_perm;
 267                 tbuf.shm_segsz  = shp->shm_segsz;
 268                 tbuf.shm_atime  = shp->shm_atime;
 269                 tbuf.shm_dtime  = shp->shm_dtime;
 270                 tbuf.shm_ctime  = shp->shm_ctime;
 271                 tbuf.shm_cpid   = shp->shm_cpid;
 272                 tbuf.shm_lpid   = shp->shm_lpid;
 273                 tbuf.shm_nattch = shp->shm_nattch;
 274                 memcpy_tofs (buf, &tbuf, sizeof(*buf));
 275                 return id;
 276         }
 277 
 278         shp = shm_segs[id = (unsigned int) shmid % SHMMNI];
 279         if (shp == IPC_UNUSED || shp == IPC_NOID)
 280                 return -EINVAL;
 281         if (shp->shm_perm.seq != (unsigned int) shmid / SHMMNI)
 282                 return -EIDRM;
 283         ipcp = &shp->shm_perm;
 284 
 285         switch (cmd) {
 286         case SHM_UNLOCK:
 287                 if (!suser())
 288                         return -EPERM;
 289                 if (!(ipcp->mode & SHM_LOCKED))
 290                         return -EINVAL;
 291                 ipcp->mode &= ~SHM_LOCKED;
 292                 break;
 293         case SHM_LOCK:
 294 /* Allow superuser to lock segment in memory */
 295 /* Should the pages be faulted in here or leave it to user? */
 296 /* need to determine interaction with current->swappable */
 297                 if (!suser())
 298                         return -EPERM;
 299                 if (ipcp->mode & SHM_LOCKED)
 300                         return -EINVAL;
 301                 ipcp->mode |= SHM_LOCKED;
 302                 break;
 303         case IPC_STAT:
 304                 if (ipcperms (ipcp, S_IRUGO))
 305                         return -EACCES;
 306                 if (!buf)
 307                         return -EFAULT;
 308                 err = verify_area (VERIFY_WRITE, buf, sizeof (*buf));
 309                 if (err)
 310                         return err;
 311                 tbuf.shm_perm   = shp->shm_perm;
 312                 tbuf.shm_segsz  = shp->shm_segsz;
 313                 tbuf.shm_atime  = shp->shm_atime;
 314                 tbuf.shm_dtime  = shp->shm_dtime;
 315                 tbuf.shm_ctime  = shp->shm_ctime;
 316                 tbuf.shm_cpid   = shp->shm_cpid;
 317                 tbuf.shm_lpid   = shp->shm_lpid;
 318                 tbuf.shm_nattch = shp->shm_nattch;
 319                 memcpy_tofs (buf, &tbuf, sizeof(*buf));
 320                 break;
 321         case IPC_SET:
 322                 if (suser() || current->euid == shp->shm_perm.uid ||
 323                     current->euid == shp->shm_perm.cuid) {
 324                         ipcp->uid = tbuf.shm_perm.uid;
 325                         ipcp->gid = tbuf.shm_perm.gid;
 326                         ipcp->mode = (ipcp->mode & ~S_IRWXUGO)
 327                                 | (tbuf.shm_perm.mode & S_IRWXUGO);
 328                         shp->shm_ctime = CURRENT_TIME;
 329                         break;
 330                 }
 331                 return -EPERM;
 332         case IPC_RMID:
 333                 if (suser() || current->euid == shp->shm_perm.uid ||
 334                     current->euid == shp->shm_perm.cuid) {
 335                         shp->shm_perm.mode |= SHM_DEST;
 336                         if (shp->shm_nattch <= 0)
 337                                 killseg (id);
 338                         break;
 339                 }
 340                 return -EPERM;
 341         default:
 342                 return -EINVAL;
 343         }
 344         return 0;
 345 }
 346 
 347 /*
 348  * The per process internal structure for managing segments is
 349  * `struct vm_area_struct'.
 350  * A shmat will add to and shmdt will remove from the list.
 351  * shmd->vm_task        the attacher
 352  * shmd->vm_start       virt addr of attach, multiple of SHMLBA
 353  * shmd->vm_end         multiple of SHMLBA
 354  * shmd->vm_next        next attach for task
 355  * shmd->vm_next_share  next attach for segment
 356  * shmd->vm_offset      offset into segment
 357  * shmd->vm_pte         signature for this attach
 358  */
 359 
 360 static struct vm_operations_struct shm_vm_ops = {
 361         shm_open,               /* open */
 362         shm_close,              /* close */
 363         NULL,                   /* unmap */
 364         NULL,                   /* protect */
 365         NULL,                   /* sync */
 366         NULL,                   /* advise */
 367         NULL,                   /* nopage (done with swapin) */
 368         NULL,                   /* wppage */
 369         NULL,                   /* swapout (hardcoded right now) */
 370         shm_swap_in             /* swapin */
 371 };
 372 
 373 /* Insert shmd into the circular list shp->attaches */
 374 static inline void insert_attach (struct shmid_ds * shp, struct vm_area_struct * shmd)
     /* [previous][next][first][last][top][bottom][index][help] */
 375 {
 376         struct vm_area_struct * attaches;
 377 
 378         if ((attaches = shp->attaches)) {
 379                 shmd->vm_next_share = attaches;
 380                 shmd->vm_prev_share = attaches->vm_prev_share;
 381                 shmd->vm_prev_share->vm_next_share = shmd;
 382                 attaches->vm_prev_share = shmd;
 383         } else
 384                 shp->attaches = shmd->vm_next_share = shmd->vm_prev_share = shmd;
 385 }
 386 
 387 /* Remove shmd from circular list shp->attaches */
 388 static inline void remove_attach (struct shmid_ds * shp, struct vm_area_struct * shmd)
     /* [previous][next][first][last][top][bottom][index][help] */
 389 {
 390         if (shmd->vm_next_share == shmd) {
 391                 if (shp->attaches != shmd) {
 392                         printk("shm_close: shm segment (id=%ld) attach list inconsistent\n",
 393                                 (shmd->vm_pte >> SHM_ID_SHIFT) & SHM_ID_MASK);
 394                         printk("shm_close: %d %08lx-%08lx %c%c%c%c %08lx %08lx\n",
 395                                 shmd->vm_task->pid, shmd->vm_start, shmd->vm_end,
 396                                 shmd->vm_flags & VM_READ ? 'r' : '-',
 397                                 shmd->vm_flags & VM_WRITE ? 'w' : '-',
 398                                 shmd->vm_flags & VM_EXEC ? 'x' : '-',
 399                                 shmd->vm_flags & VM_SHARED ? 's' : 'p',
 400                                 shmd->vm_offset, shmd->vm_pte);
 401                 }
 402                 shp->attaches = NULL;
 403         } else {
 404                 if (shp->attaches == shmd)
 405                         shp->attaches = shmd->vm_next_share;
 406                 shmd->vm_prev_share->vm_next_share = shmd->vm_next_share;
 407                 shmd->vm_next_share->vm_prev_share = shmd->vm_prev_share;
 408         }
 409 }
 410 
 411 /*
 412  * check range is unmapped, ensure page tables exist
 413  * mark page table entries with shm_sgn.
 414  * if remap != 0 the range is remapped.
 415  */
 416 static int shm_map (struct vm_area_struct *shmd, int remap)
     /* [previous][next][first][last][top][bottom][index][help] */
 417 {
 418         pgd_t *page_dir;
 419         pte_t *page_table;
 420         unsigned long tmp, shm_sgn;
 421 
 422         /* check that the range is unmapped */
 423         if (!remap)
 424                 for (tmp = shmd->vm_start; tmp < shmd->vm_end; tmp += PAGE_SIZE) {
 425                         page_dir = PAGE_DIR_OFFSET(shmd->vm_task,tmp);
 426                         if (pgd_none(*page_dir))
 427                                 continue;
 428                         if (pgd_bad(*page_dir)) {
 429                                 printk("bad ipc page directory entry %08lx\n", pgd_val(*page_dir));
 430                                 pgd_clear(page_dir);
 431                                 continue;
 432                         }
 433                         page_table = (pte_t *) pgd_page(*page_dir);     
 434                         page_table += ((tmp >> PAGE_SHIFT) & (PTRS_PER_PAGE-1));
 435                         if (!pte_none(*page_table)) {
 436                                 /* printk("shmat() -> EINVAL because address 0x%lx is already mapped.\n",tmp); */
 437                                 return -EINVAL;
 438                         }
 439                 }
 440 
 441         /* clear old mappings */
 442         do_munmap(shmd->vm_start, shmd->vm_end - shmd->vm_start);
 443 
 444         /* add new mapping */
 445         insert_vm_struct(current, shmd);
 446         merge_segments(current, shmd->vm_start, shmd->vm_end);
 447 
 448         /* check that the range has page_tables */
 449         for (tmp = shmd->vm_start; tmp < shmd->vm_end; tmp += PAGE_SIZE) {
 450                 page_dir = PAGE_DIR_OFFSET(shmd->vm_task,tmp);
 451                 if (!pgd_none(*page_dir)) {
 452                         page_table = (pte_t *) pgd_page(*page_dir);
 453                         page_table += ((tmp >> PAGE_SHIFT) & (PTRS_PER_PAGE-1));
 454                         if (!pte_none(*page_table)) {
 455                                 if (pte_present(*page_table)) {
 456                                         --current->mm->rss;
 457                                         free_page (pte_page(*page_table));
 458                                 } else
 459                                         swap_free(pte_val(*page_table));
 460                                 pte_clear(page_table);
 461                         }
 462                 } else {
 463                         if (!(page_table = (pte_t *) get_free_page(GFP_KERNEL)))
 464                                 return -ENOMEM;
 465                         pgd_set(page_dir, page_table);
 466                         tmp |= ((PAGE_SIZE << 10) - PAGE_SIZE);
 467                 }
 468         }
 469 
 470         /* map page range */
 471         shm_sgn = shmd->vm_pte + ((shmd->vm_offset >> PAGE_SHIFT) << SHM_IDX_SHIFT);
 472         for (tmp = shmd->vm_start; tmp < shmd->vm_end; tmp += PAGE_SIZE,
 473              shm_sgn += (1 << SHM_IDX_SHIFT)) {
 474                 page_dir = PAGE_DIR_OFFSET(shmd->vm_task,tmp);
 475                 page_table = (pte_t *) pgd_page(*page_dir);
 476                 page_table += (tmp >> PAGE_SHIFT) & (PTRS_PER_PAGE-1);
 477                 pte_val(*page_table) = shm_sgn;
 478         }
 479         invalidate();
 480         return 0;
 481 }
 482 
 483 /*
 484  * Fix shmaddr, allocate descriptor, map shm, add attach descriptor to lists.
 485  */
 486 int sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *raddr)
     /* [previous][next][first][last][top][bottom][index][help] */
 487 {
 488         struct shmid_ds *shp;
 489         struct vm_area_struct *shmd;
 490         int err;
 491         unsigned int id;
 492         unsigned long addr;
 493 
 494         if (shmid < 0) {
 495                 /* printk("shmat() -> EINVAL because shmid = %d < 0\n",shmid); */
 496                 return -EINVAL;
 497         }
 498 
 499         shp = shm_segs[id = (unsigned int) shmid % SHMMNI];
 500         if (shp == IPC_UNUSED || shp == IPC_NOID) {
 501                 /* printk("shmat() -> EINVAL because shmid = %d is invalid\n",shmid); */
 502                 return -EINVAL;
 503         }
 504 
 505         if (!(addr = (ulong) shmaddr)) {
 506                 if (shmflg & SHM_REMAP)
 507                         return -EINVAL;
 508                 if (!(addr = get_unmapped_area(shp->shm_segsz)))
 509                         return -ENOMEM;
 510         } else if (addr & (SHMLBA-1)) {
 511                 if (shmflg & SHM_RND)
 512                         addr &= ~(SHMLBA-1);       /* round down */
 513                 else
 514                         return -EINVAL;
 515         }
 516         if ((addr > current->mm->start_stack - 16384 - PAGE_SIZE*shp->shm_npages)) {
 517                 /* printk("shmat() -> EINVAL because segment intersects stack\n"); */
 518                 return -EINVAL;
 519         }
 520         if (!(shmflg & SHM_REMAP))
 521                 if ((shmd = find_vma_intersection(current, addr, addr + shp->shm_segsz))) {
 522                         /* printk("shmat() -> EINVAL because the interval [0x%lx,0x%lx) intersects an already mapped interval [0x%lx,0x%lx).\n",
 523                                 addr, addr + shp->shm_segsz, shmd->vm_start, shmd->vm_end); */
 524                         return -EINVAL;
 525                 }
 526 
 527         if (ipcperms(&shp->shm_perm, shmflg & SHM_RDONLY ? S_IRUGO : S_IRUGO|S_IWUGO))
 528                 return -EACCES;
 529         if (shp->shm_perm.seq != (unsigned int) shmid / SHMMNI)
 530                 return -EIDRM;
 531 
 532         shmd = (struct vm_area_struct *) kmalloc (sizeof(*shmd), GFP_KERNEL);
 533         if (!shmd)
 534                 return -ENOMEM;
 535         if ((shp != shm_segs[id]) || (shp->shm_perm.seq != (unsigned int) shmid / SHMMNI)) {
 536                 kfree(shmd);
 537                 return -EIDRM;
 538         }
 539 
 540         shmd->vm_pte = (SHM_SWP_TYPE << 1) | (id << SHM_ID_SHIFT);
 541         shmd->vm_start = addr;
 542         shmd->vm_end = addr + shp->shm_npages * PAGE_SIZE;
 543         shmd->vm_task = current;
 544         shmd->vm_page_prot = (shmflg & SHM_RDONLY) ? PAGE_READONLY : PAGE_SHARED;
 545         shmd->vm_flags = VM_SHM | VM_MAYSHARE | VM_SHARED
 546                          | VM_MAYREAD | VM_MAYEXEC | VM_READ | VM_EXEC
 547                          | ((shmflg & SHM_RDONLY) ? 0 : VM_MAYWRITE | VM_WRITE);
 548         shmd->vm_next_share = shmd->vm_prev_share = NULL;
 549         shmd->vm_inode = NULL;
 550         shmd->vm_offset = 0;
 551         shmd->vm_ops = &shm_vm_ops;
 552 
 553         shp->shm_nattch++;            /* prevent destruction */
 554         if ((err = shm_map (shmd, shmflg & SHM_REMAP))) {
 555                 if (--shp->shm_nattch <= 0 && shp->shm_perm.mode & SHM_DEST)
 556                         killseg(id);
 557                 kfree(shmd);
 558                 return err;
 559         }
 560 
 561         insert_attach(shp,shmd);  /* insert shmd into shp->attaches */
 562 
 563         shp->shm_lpid = current->pid;
 564         shp->shm_atime = CURRENT_TIME;
 565 
 566         *raddr = addr;
 567         return 0;
 568 }
 569 
 570 /* This is called by fork, once for every shm attach. */
 571 static void shm_open (struct vm_area_struct *shmd)
     /* [previous][next][first][last][top][bottom][index][help] */
 572 {
 573         unsigned int id;
 574         struct shmid_ds *shp;
 575 
 576         id = (shmd->vm_pte >> SHM_ID_SHIFT) & SHM_ID_MASK;
 577         shp = shm_segs[id];
 578         if (shp == IPC_UNUSED) {
 579                 printk("shm_open: unused id=%d PANIC\n", id);
 580                 return;
 581         }
 582         insert_attach(shp,shmd);  /* insert shmd into shp->attaches */
 583         shp->shm_nattch++;
 584         shp->shm_atime = CURRENT_TIME;
 585         shp->shm_lpid = current->pid;
 586 }
 587 
 588 /*
 589  * remove the attach descriptor shmd.
 590  * free memory for segment if it is marked destroyed.
 591  * The descriptor has already been removed from the current->mm->mmap list
 592  * and will later be kfree()d.
 593  */
 594 static void shm_close (struct vm_area_struct *shmd)
     /* [previous][next][first][last][top][bottom][index][help] */
 595 {
 596         struct shmid_ds *shp;
 597         int id;
 598 
 599         unmap_page_range (shmd->vm_start, shmd->vm_end - shmd->vm_start);
 600 
 601         /* remove from the list of attaches of the shm segment */
 602         id = (shmd->vm_pte >> SHM_ID_SHIFT) & SHM_ID_MASK;
 603         shp = shm_segs[id];
 604         remove_attach(shp,shmd);  /* remove from shp->attaches */
 605         shp->shm_lpid = current->pid;
 606         shp->shm_dtime = CURRENT_TIME;
 607         if (--shp->shm_nattch <= 0 && shp->shm_perm.mode & SHM_DEST)
 608                 killseg (id);
 609 }
 610 
 611 /*
 612  * detach and kill segment if marked destroyed.
 613  * The work is done in shm_close.
 614  */
 615 int sys_shmdt (char *shmaddr)
     /* [previous][next][first][last][top][bottom][index][help] */
 616 {
 617         struct vm_area_struct *shmd, *shmdnext;
 618 
 619         for (shmd = current->mm->mmap; shmd; shmd = shmdnext) {
 620                 shmdnext = shmd->vm_next;
 621                 if (shmd->vm_ops == &shm_vm_ops
 622                     && shmd->vm_start - shmd->vm_offset == (ulong) shmaddr)
 623                         do_munmap(shmd->vm_start, shmd->vm_end - shmd->vm_start);
 624         }
 625         return 0;
 626 }
 627 
 628 /*
 629  * page not present ... go through shm_pages
 630  */
 631 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] */
 632 {
 633         pte_t pte;
 634         struct shmid_ds *shp;
 635         unsigned int id, idx;
 636 
 637         id = (code >> SHM_ID_SHIFT) & SHM_ID_MASK;
 638         if (id != ((shmd->vm_pte >> SHM_ID_SHIFT) & SHM_ID_MASK)) {
 639                 printk ("shm_swap_in: code id = %d and shmd id = %ld differ\n",
 640                         id, (shmd->vm_pte >> SHM_ID_SHIFT) & SHM_ID_MASK);
 641                 return BAD_PAGE;
 642         }
 643         if (id > max_shmid) {
 644                 printk ("shm_swap_in: id=%d too big. proc mem corrupted\n", id);
 645                 return BAD_PAGE;
 646         }
 647         shp = shm_segs[id];
 648         if (shp == IPC_UNUSED || shp == IPC_NOID) {
 649                 printk ("shm_swap_in: id=%d invalid. Race.\n", id);
 650                 return BAD_PAGE;
 651         }
 652         idx = (code >> SHM_IDX_SHIFT) & SHM_IDX_MASK;
 653         if (idx != (offset >> PAGE_SHIFT)) {
 654                 printk ("shm_swap_in: code idx = %u and shmd idx = %lu differ\n",
 655                         idx, offset >> PAGE_SHIFT);
 656                 return BAD_PAGE;
 657         }
 658         if (idx >= shp->shm_npages) {
 659                 printk ("shm_swap_in : too large page index. id=%d\n", id);
 660                 return BAD_PAGE;
 661         }
 662 
 663         pte_val(pte) = shp->shm_pages[idx];
 664         if (!pte_present(pte)) {
 665                 unsigned long page = get_free_page(GFP_KERNEL);
 666                 if (!page) {
 667                         oom(current);
 668                         return BAD_PAGE;
 669                 }
 670                 pte_val(pte) = shp->shm_pages[idx];
 671                 if (pte_present(pte)) {
 672                         free_page (page);
 673                         goto done;
 674                 }
 675                 if (!pte_none(pte)) {
 676                         read_swap_page(pte_val(pte), (char *) page);
 677                         pte_val(pte) = shp->shm_pages[idx];
 678                         if (pte_present(pte))  {
 679                                 free_page (page);
 680                                 goto done;
 681                         }
 682                         swap_free(pte_val(pte));
 683                         shm_swp--;
 684                 }
 685                 shm_rss++;
 686                 pte = pte_mkdirty(mk_pte(page, PAGE_SHARED));
 687                 shp->shm_pages[idx] = pte_val(pte);
 688         } else
 689                 --current->mm->maj_flt;  /* was incremented in do_no_page */
 690 
 691 done:
 692         current->mm->min_flt++;
 693         mem_map[MAP_NR(pte_page(pte))]++;
 694         return pte_modify(pte, shmd->vm_page_prot);
 695 }
 696 
 697 /*
 698  * Goes through counter = (shm_rss << prio) present shm pages.
 699  */
 700 static unsigned long swap_id = 0; /* currently being swapped */
 701 static unsigned long swap_idx = 0; /* next to swap */
 702 
 703 int shm_swap (int prio)
     /* [previous][next][first][last][top][bottom][index][help] */
 704 {
 705         pte_t page;
 706         struct shmid_ds *shp;
 707         struct vm_area_struct *shmd;
 708         unsigned int swap_nr;
 709         unsigned long id, idx, invalid = 0;
 710         int counter;
 711 
 712         counter = shm_rss >> prio;
 713         if (!counter || !(swap_nr = get_swap_page()))
 714                 return 0;
 715 
 716  check_id:
 717         shp = shm_segs[swap_id];
 718         if (shp == IPC_UNUSED || shp == IPC_NOID || shp->shm_perm.mode & SHM_LOCKED ) {
 719                 swap_idx = 0;
 720                 if (++swap_id > max_shmid)
 721                         swap_id = 0;
 722                 goto check_id;
 723         }
 724         id = swap_id;
 725 
 726  check_table:
 727         idx = swap_idx++;
 728         if (idx >= shp->shm_npages) {
 729                 swap_idx = 0;
 730                 if (++swap_id > max_shmid)
 731                         swap_id = 0;
 732                 goto check_id;
 733         }
 734 
 735         pte_val(page) = shp->shm_pages[idx];
 736         if (!pte_present(page))
 737                 goto check_table;
 738         swap_attempts++;
 739 
 740         if (--counter < 0) { /* failed */
 741                 if (invalid)
 742                         invalidate();
 743                 swap_free (swap_nr);
 744                 return 0;
 745         }
 746         for (shmd = shp->attaches; ; ) {
 747             do {
 748                 pgd_t *page_dir;
 749                 pte_t *page_table, pte;
 750                 unsigned long tmp;
 751 
 752                 if ((shmd->vm_pte >> SHM_ID_SHIFT & SHM_ID_MASK) != id) {
 753                         printk ("shm_swap: id=%ld does not match shmd->vm_pte.id=%ld\n", id, shmd->vm_pte >> SHM_ID_SHIFT & SHM_ID_MASK);
 754                         continue;
 755                 }
 756                 tmp = shmd->vm_start + (idx << PAGE_SHIFT) - shmd->vm_offset;
 757                 if (!(tmp >= shmd->vm_start && tmp < shmd->vm_end))
 758                         continue;
 759                 page_dir = PAGE_DIR_OFFSET(shmd->vm_task,tmp);
 760                 if (pgd_none(*page_dir) || pgd_bad(*page_dir)) {
 761                         printk("shm_swap: bad pgtbl! id=%ld start=%lx idx=%ld\n",
 762                                         id, shmd->vm_start, idx);
 763                         pgd_clear(page_dir);
 764                         continue;
 765                 }
 766                 page_table = (pte_t *) pgd_page(*page_dir);
 767                 page_table += ((tmp >> PAGE_SHIFT) & (PTRS_PER_PAGE-1));
 768                 pte = *page_table;
 769                 if (!pte_present(pte))
 770                         continue;
 771                 if (pte_young(pte)) {
 772                         *page_table = pte_mkold(pte);
 773                         continue;
 774                 }
 775                 if (pte_page(pte) != pte_page(page))
 776                         printk("shm_swap_out: page and pte mismatch\n");
 777                 pte_val(*page_table) = shmd->vm_pte | idx << SHM_IDX_SHIFT;
 778                 mem_map[MAP_NR(pte_page(pte))]--;
 779                 shmd->vm_task->mm->rss--;
 780                 invalid++;
 781             /* continue looping through circular list */
 782             } while (0);
 783             if ((shmd = shmd->vm_next_share) == shp->attaches)
 784                 break;
 785         }
 786 
 787         if (mem_map[MAP_NR(pte_page(page))] != 1)
 788                 goto check_table;
 789         shp->shm_pages[idx] = swap_nr;
 790         if (invalid)
 791                 invalidate();
 792         write_swap_page (swap_nr, (char *) pte_page(page));
 793         free_page(pte_page(page));
 794         swap_successes++;
 795         shm_swp++;
 796         shm_rss--;
 797         return 1;
 798 }

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