home *** CD-ROM | disk | FTP | other *** search
/ Il CD di internet / CD.iso / SOURCE / KERNEL-S / V1.2 / LINUX-1.2 / LINUX-1 / linux / ipc / shm.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-02-25  |  20.4 KB  |  774 lines

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