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