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