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