netlib.narod.ru< Назад | Оглавление | Далее >

ipc/shm.c

21431 /*
21432  * linux/ipc/shm.c
21433  * Copyright (C) 1992, 1993 Krishna Balasubramanian
21434  *         Many improvements/fixes by Bruno Haible.
21435  * Replaced `struct shm_desc' by `struct vm_area_struct',
21436  * July 1994.
21437  * Fixed the shm swap deallocation (shm_unuse()), August
21438  * 1998 Andrea Arcangeli.
21439  */
21440 
21441 #include <linux/malloc.h>
21442 #include <linux/shm.h>
21443 #include <linux/swap.h>
21444 #include <linux/smp_lock.h>
21445 #include <linux/init.h>
21446 #include <linux/vmalloc.h>
21447 
21448 #include <asm/uaccess.h>
21449 #include <asm/pgtable.h>
21450 
21451 extern int ipcperms(struct ipc_perm *ipcp, short shmflg);
21452 extern unsigned long get_swap_page(void);
21453 static int findkey(key_t key);
21454 static int newseg(key_t key, int shmflg, int size);
21455 static int shm_map(struct vm_area_struct *shmd);
21456 static void killseg(int id);
21457 static void shm_open(struct vm_area_struct *shmd);
21458 static void shm_close(struct vm_area_struct *shmd);
21459 static pte_t shm_swap_in(struct vm_area_struct *,
21460                          unsigned long, unsigned long);
21461 
21462 /* total number of shared memory pages */
21463 static int shm_tot = 0;
21464 /* number of shared memory pages that are in memory */
21465 static int shm_rss = 0;
21466 /* number of shared memory pages that are in swap */
21467 static int shm_swp = 0;
21468 /* every used id is <= max_shmid */
21469 static int max_shmid = 0;
21470 /* calling findkey() may need to wait */
21471 static struct wait_queue *shm_lock = NULL;
21472 static struct shmid_kernel *shm_segs[SHMMNI];
21473 
21474 /* incremented, for recognizing stale ids */
21475 static unsigned short shm_seq = 0;
21476 
21477 /* some statistics */
21478 static ulong swap_attempts = 0;
21479 static ulong swap_successes = 0;
21480 static ulong used_segs = 0;
21481 
21482 void __init shm_init (void)
21483 {
21484   int id;
21485 
21486   for (id = 0; id < SHMMNI; id++)
21487     shm_segs[id] = (struct shmid_kernel *) IPC_UNUSED;
21488   shm_tot = shm_rss = shm_seq = max_shmid = used_segs =0;
21489   shm_lock = NULL;
21490   return;
21491 }
21492 
21493 static int findkey (key_t key)
21494 {
21495   int id;
21496   struct shmid_kernel *shp;
21497 
21498   for (id = 0; id <= max_shmid; id++) {
21499     while ((shp = shm_segs[id]) == IPC_NOID)
21500       sleep_on (&shm_lock);
21501     if (shp == IPC_UNUSED)
21502       continue;
21503     if (key == shp->u.shm_perm.key)
21504       return id;
21505   }
21506   return -1;
21507 }
21508 
21509 /* allocate new shmid_kernel and pgtable. protected by
21510  * shm_segs[id] = NOID.  */
 Комментарий
21511 static int newseg (key_t key, int shmflg, int size)
21512 {
21513   struct shmid_kernel *shp;
21514   int numpages = (size + PAGE_SIZE -1) >> PAGE_SHIFT;
21515   int id, i;
21516 
21517   if (size < SHMMIN)
21518     return -EINVAL;
21519   if (shm_tot + numpages >= SHMALL)
21520     return -ENOSPC;
21521   for (id = 0; id < SHMMNI; id++)
21522     if (shm_segs[id] == IPC_UNUSED) {
21523       shm_segs[id] = (struct shmid_kernel *) IPC_NOID;
21524       goto found;
21525     }
21526   return -ENOSPC;
21527 
21528 found:
21529   shp = (struct shmid_kernel *) kmalloc(sizeof(*shp),
21530                                         GFP_KERNEL);
21531   if (!shp) {
21532     shm_segs[id] = (struct shmid_kernel *) IPC_UNUSED;
21533     wake_up (&shm_lock);
21534     return -ENOMEM;
21535   }
21536 
21537   shp->shm_pages =
21538     (ulong *) vmalloc(numpages*sizeof(ulong));
21539   if (!shp->shm_pages) {
21540     shm_segs[id] = (struct shmid_kernel *) IPC_UNUSED;
21541     wake_up (&shm_lock);
21542     kfree(shp);
21543     return -ENOMEM;
21544   }
21545 
21546   for (i = 0; i < numpages; shp->shm_pages[i++] = 0);
21547   shm_tot += numpages;
21548   shp->u.shm_perm.key = key;
21549   shp->u.shm_perm.mode = (shmflg & S_IRWXUGO);
21550   shp->u.shm_perm.cuid = shp->u.shm_perm.uid =
21551     current->euid;
21552   shp->u.shm_perm.cgid = shp->u.shm_perm.gid =
21553     current->egid;
21554   shp->u.shm_perm.seq = shm_seq;
21555   shp->u.shm_segsz = size;
21556   shp->u.shm_cpid = current->pid;
21557   shp->attaches = NULL;
21558   shp->u.shm_lpid = shp->u.shm_nattch = 0;
21559   shp->u.shm_atime = shp->u.shm_dtime = 0;
21560   shp->u.shm_ctime = CURRENT_TIME;
21561   shp->shm_npages = numpages;
21562 
21563   if (id > max_shmid)
21564     max_shmid = id;
21565   shm_segs[id] = shp;
21566   used_segs++;
21567   wake_up (&shm_lock);
21568   return (unsigned int) shp->u.shm_perm.seq*SHMMNI + id;
21569 }
21570 
21571 int shmmax = SHMMAX;
21572 
 Комментарий
21573 asmlinkage int sys_shmget(key_t key,int size,int shmflg)
21574 {
21575   struct shmid_kernel *shp;
21576   int err, id = 0;
21577 
21578   down(&current->mm->mmap_sem);
21579   lock_kernel();
21580   if (size < 0 || size > shmmax) {
21581     err = -EINVAL;
21582   } else if (key == IPC_PRIVATE) {
21583     err = newseg(key, shmflg, size);
21584   } else if ((id = findkey (key)) == -1) {
21585     if (!(shmflg & IPC_CREAT))
21586       err = -ENOENT;
21587     else
21588       err = newseg(key, shmflg, size);
21589   } else if ((shmflg & IPC_CREAT) &&
21590              (shmflg & IPC_EXCL)) {
21591     err = -EEXIST;
21592   } else {
21593     shp = shm_segs[id];
21594     if (shp->u.shm_perm.mode & SHM_DEST)
21595       err = -EIDRM;
21596     else if (size > shp->u.shm_segsz)
21597       err = -EINVAL;
21598     else if (ipcperms (&shp->u.shm_perm, shmflg))
21599       err = -EACCES;
21600     else
21601       err = (int) shp->u.shm_perm.seq * SHMMNI + id;
21602   }
21603   unlock_kernel();
21604   up(&current->mm->mmap_sem);
21605   return err;
21606 }
21607 
21608 /* Only called after testing nattch and SHM_DEST.  Here
21609  * pages, pgtable and shmid_kernel are freed.  */
 Комментарий
21610 static void killseg (int id)
21611 {
21612   struct shmid_kernel *shp;
21613   int i, numpages;
21614 
21615   shp = shm_segs[id];
21616   if (shp == IPC_NOID || shp == IPC_UNUSED) {
21617     printk("shm nono: killseg called on unused seg "
21618            "id=%d\n", id);
21619     return;
21620   }
21621   shp->u.shm_perm.seq++;     /* for shmat */
21622   /* increment, but avoid overflow */
21623   shm_seq = (shm_seq+1) % ((unsigned)(1<<31)/SHMMNI);
21624   shm_segs[id] = (struct shmid_kernel *) IPC_UNUSED;
21625   used_segs--;
21626   if (id == max_shmid)
21627     while (max_shmid &&
21628            (shm_segs[--max_shmid] == IPC_UNUSED));
21629   if (!shp->shm_pages) {
21630     printk("shm nono: killseg shp->pages=NULL. "
21631            "id=%d\n", id);
21632     return;
21633   }
21634   numpages = shp->shm_npages;
 Комментарий
21635   for (i = 0; i < numpages ; i++) {
21636     pte_t pte;
21637     pte = __pte(shp->shm_pages[i]);
21638     if (pte_none(pte))
21639       continue;
21640     if (pte_present(pte)) {
21641       free_page (pte_page(pte));
21642       shm_rss--;
21643     } else {
21644       swap_free(pte_val(pte));
21645       shm_swp--;
21646     }
21647   }
21648   vfree(shp->shm_pages);
21649   shm_tot -= numpages;
21650   kfree(shp);
21651   return;
21652 }
21653 
 Комментарий
21654 asmlinkage int sys_shmctl (int shmid, int cmd,
21655                            struct shmid_ds *buf)
21656 {
21657   struct shmid_ds tbuf;
21658   struct shmid_kernel *shp;
21659   struct ipc_perm *ipcp;
21660   int id, err = -EINVAL;
21661 
21662   lock_kernel();
21663   if (cmd < 0 || shmid < 0)
21664     goto out;
21665   if (cmd == IPC_SET) {
21666     err = -EFAULT;
21667     if(copy_from_user (&tbuf, buf, sizeof (*buf)))
21668       goto out;
21669   }
21670 
21671   switch (cmd) { /* replace with proc interface ? */
21672   case IPC_INFO:
21673   {
21674     struct shminfo shminfo;
21675     err = -EFAULT;
21676     if (!buf)
21677       goto out;
21678     shminfo.shmmni = SHMMNI;
21679     shminfo.shmmax = shmmax;
21680     shminfo.shmmin = SHMMIN;
21681     shminfo.shmall = SHMALL;
21682     shminfo.shmseg = SHMSEG;
21683     if (copy_to_user(buf, &shminfo,
21684                      sizeof(struct shminfo)))
21685       goto out;
21686     err = max_shmid;
21687     goto out;
21688   }
21689   case SHM_INFO:
21690   {
21691     struct shm_info shm_info;
21692     err = -EFAULT;
21693     shm_info.used_ids = used_segs;
21694     shm_info.shm_rss = shm_rss;
21695     shm_info.shm_tot = shm_tot;
21696     shm_info.shm_swp = shm_swp;
21697     shm_info.swap_attempts = swap_attempts;
21698     shm_info.swap_successes = swap_successes;
21699     if(copy_to_user (buf, &shm_info, sizeof(shm_info)))
21700       goto out;
21701     err = max_shmid;
21702     goto out;
21703   }
21704   case SHM_STAT:
21705     err = -EINVAL;
21706     if (shmid > max_shmid)
21707       goto out;
21708     shp = shm_segs[shmid];
21709     if (shp == IPC_UNUSED || shp == IPC_NOID)
21710       goto out;
21711     if (ipcperms (&shp->u.shm_perm, S_IRUGO))
21712       goto out;
21713     id =
21714       (unsigned int)shp->u.shm_perm.seq * SHMMNI + shmid;
21715     err = -EFAULT;
21716     if(copy_to_user (buf, &shp->u, sizeof(*buf)))
21717       goto out;
21718     err = id;
21719     goto out;
21720   }
21721 
21722   shp = shm_segs[id = (unsigned int) shmid % SHMMNI];
21723   err = -EINVAL;
21724   if (shp == IPC_UNUSED || shp == IPC_NOID)
21725     goto out;
21726   err = -EIDRM;
21727   if (shp->u.shm_perm.seq !=
21728       (unsigned int) shmid / SHMMNI)
21729     goto out;
21730   ipcp = &shp->u.shm_perm;
21731 
21732   switch (cmd) {
21733   case SHM_UNLOCK:
21734     err = -EPERM;
21735     if (!capable(CAP_IPC_LOCK))
21736       goto out;
21737     err = -EINVAL;
21738     if (!(ipcp->mode & SHM_LOCKED))
21739       goto out;
21740     ipcp->mode &= ~SHM_LOCKED;
21741     break;
21742   case SHM_LOCK:
21743 /* Allow superuser to lock segment in memory */
21744 /* Should the pages be faulted in here or leave it to
21745  * user? */
21746 /* need to determine interaction w/ current->swappable */
21747     err = -EPERM;
21748     if (!capable(CAP_IPC_LOCK))
21749       goto out;
21750     err = -EINVAL;
21751     if (ipcp->mode & SHM_LOCKED)
21752       goto out;
21753     ipcp->mode |= SHM_LOCKED;
21754     break;
21755   case IPC_STAT:
21756     err = -EACCES;
21757     if (ipcperms (ipcp, S_IRUGO))
21758       goto out;
21759     err = -EFAULT;
21760     if(copy_to_user (buf, &shp->u, sizeof(shp->u)))
21761       goto out;
21762     break;
21763   case IPC_SET:
21764     if (current->euid == shp->u.shm_perm.uid ||
21765         current->euid == shp->u.shm_perm.cuid ||
21766         capable(CAP_SYS_ADMIN)) {
21767       ipcp->uid = tbuf.shm_perm.uid;
21768       ipcp->gid = tbuf.shm_perm.gid;
21769       ipcp->mode = (ipcp->mode & ~S_IRWXUGO)
21770         | (tbuf.shm_perm.mode & S_IRWXUGO);
21771       shp->u.shm_ctime = CURRENT_TIME;
21772       break;
21773     }
21774     err = -EPERM;
21775     goto out;
21776   case IPC_RMID:
21777     if (current->euid == shp->u.shm_perm.uid ||
21778         current->euid == shp->u.shm_perm.cuid ||
21779         capable(CAP_SYS_ADMIN)) {
21780       shp->u.shm_perm.mode |= SHM_DEST;
21781       if (shp->u.shm_nattch <= 0)
21782         killseg (id);
21783       break;
21784     }
21785     err = -EPERM;
21786     goto out;
21787   default:
21788     err = -EINVAL;
21789     goto out;
21790   }
21791   err = 0;
21792 out:
21793   unlock_kernel();
21794   return err;
21795 }
21796 
21797 /* The per process internal structure for managing
21798  * segments is `struct vm_area_struct'.  A shmat will add
21799  * to and shmdt will remove from the list.
21800  * shmd->vm_mm          the attacher
21801  * shmd->vm_start       virt addr of attach,
21802  *                      multiple of SHMLBA
21803  * shmd->vm_end         multiple of SHMLBA
21804  * shmd->vm_next        next attach for task
21805  * shmd->vm_next_share  next attach for segment
21806  * shmd->vm_offset      offset into segment
21807  * shmd->vm_pte         signature for this attach */
21808 
21809 static struct vm_operations_struct shm_vm_ops = {
21810   shm_open, /* open - callback for a new vm-area open */
21811   shm_close, /* close - CB for when vm-area is released*/
21812   NULL,      /* no need to sync pages at unmap */
21813   NULL,      /* protect */
21814   NULL,      /* sync */
21815   NULL,      /* advise */
21816   NULL,      /* nopage (done with swapin) */
21817   NULL,      /* wppage */
21818   NULL,      /* swapout (hardcoded right now) */
21819   shm_swap_in  /* swapin */
21820 };
21821 
21822 /* Insert shmd into the list shp->attaches */
 Комментарий
21823 static inline void insert_attach(
21824   struct shmid_kernel *shp, struct vm_area_struct *shmd)
21825 {
21826   if((shmd->vm_next_share = shp->attaches) != NULL)
21827     shp->attaches->vm_pprev_share = &shmd->vm_next_share;
21828   shp->attaches = shmd;
21829   shmd->vm_pprev_share = &shp->attaches;
21830 }
21831 
21832 /* Remove shmd from list shp->attaches */
 Комментарий
21833 static inline void remove_attach(
21834   struct shmid_kernel *shp, struct vm_area_struct *shmd)
21835 {
21836   if(shmd->vm_next_share)
21837     shmd->vm_next_share->vm_pprev_share =
21838       shmd->vm_pprev_share;
21839   *shmd->vm_pprev_share = shmd->vm_next_share;
21840 }
21841 
21842 /* ensure page tables exist
21843  * mark page table entries with shm_sgn. */
21844 static int shm_map (struct vm_area_struct *shmd)
21845 {
21846   pgd_t *page_dir;
21847   pmd_t *page_middle;
21848   pte_t *page_table;
21849   unsigned long tmp, shm_sgn;
21850   int error;
21851 
21852   /* clear old mappings */
21853   do_munmap(shmd->vm_start,
21854             shmd->vm_end - shmd->vm_start);
21855 
21856   /* add new mapping */
21857   tmp = shmd->vm_end - shmd->vm_start;
21858   if((current->mm->total_vm << PAGE_SHIFT) + tmp
21859      > (unsigned long) current->rlim[RLIMIT_AS].rlim_cur)
21860     return -ENOMEM;
21861   current->mm->total_vm += tmp >> PAGE_SHIFT;
21862   insert_vm_struct(current->mm, shmd);
21863   merge_segments(current->mm, shmd->vm_start,
21864                  shmd->vm_end);
21865 
21866   /* map page range */
21867   error = 0;
21868   shm_sgn = shmd->vm_pte +
21869     SWP_ENTRY(0, (shmd->vm_offset >> PAGE_SHIFT)
21870                  << SHM_IDX_SHIFT);
21871   flush_cache_range(shmd->vm_mm, shmd->vm_start,
21872                     shmd->vm_end);
21873   for (tmp = shmd->vm_start;
21874        tmp < shmd->vm_end;
21875        tmp += PAGE_SIZE,
21876          shm_sgn += SWP_ENTRY(0, 1 << SHM_IDX_SHIFT))
21877   {
21878     page_dir = pgd_offset(shmd->vm_mm,tmp);
21879     page_middle = pmd_alloc(page_dir,tmp);
21880     if (!page_middle) {
21881       error = -ENOMEM;
21882       break;
21883     }
21884     page_table = pte_alloc(page_middle,tmp);
21885     if (!page_table) {
21886       error = -ENOMEM;
21887       break;
21888     }
21889     set_pte(page_table, __pte(shm_sgn));
21890   }
21891   flush_tlb_range(shmd->vm_mm, shmd->vm_start,
21892                   shmd->vm_end);
21893   return error;
21894 }
21895 
21896 /* Fix shmaddr, allocate descriptor, map shm, add attach
21897  * descriptor to lists.  */
 Комментарий
21898 asmlinkage int sys_shmat (int shmid, char *shmaddr,
21899                           int shmflg, ulong *raddr)
21900 {
21901   struct shmid_kernel *shp;
21902   struct vm_area_struct *shmd;
21903   int err = -EINVAL;
21904   unsigned int id;
21905   unsigned long addr;
21906   unsigned long len;
21907 
21908   down(&current->mm->mmap_sem);
21909   lock_kernel();
21910   if (shmid < 0) {
21911     /* printk("shmat() -> EINVAL because shmid = "
21912      *        "%d < 0\n", shmid); */
21913     goto out;
21914   }
21915 
21916   shp = shm_segs[id = (unsigned int) shmid % SHMMNI];
21917   if (shp == IPC_UNUSED || shp == IPC_NOID) {
21918     /* printk("shmat() -> EINVAL because shmid = %d "
21919      *        "is invalid\n", shmid); */
21920     goto out;
21921   }
21922 
21923   if (!(addr = (ulong) shmaddr)) {
21924     if (shmflg & SHM_REMAP)
21925       goto out;
21926     err = -ENOMEM;
21927     addr = 0;
21928   again:
21929     if (!(addr = get_unmapped_area(addr,
21930                                    shp->u.shm_segsz)))
21931       goto out;
21932     if (addr & (SHMLBA - 1)) {
21933       addr = (addr + (SHMLBA - 1)) & ~(SHMLBA - 1);
21934       goto again;
21935     }
21936   } else if (addr & (SHMLBA-1)) {
21937     if (shmflg & SHM_RND)
21938       addr &= ~(SHMLBA-1);       /* round down */
21939     else
21940       goto out;
21941   }
21942   /* Check if addr exceeds TASK_SIZE (from do_mmap) */
21943   len = PAGE_SIZE*shp->shm_npages;
21944   err = -EINVAL;
21945   if (addr >= TASK_SIZE || len > TASK_SIZE ||
21946       addr > TASK_SIZE - len)
21947     goto out;
21948   /* If shm segment goes below stack, make sure there is
21949    * some space left for the stack to grow (presently 4
21950    * pages).  */
 Комментарий
21951   if (addr < current->mm->start_stack &&
21952       addr > current->mm->start_stack -
21953              PAGE_SIZE*(shp->shm_npages + 4))
21954   {
21955     /* printk("shmat() -> EINVAL because segment "
21956      *        "intersects stack\n"); */
21957     goto out;
21958   }
21959   if (!(shmflg & SHM_REMAP))
21960     if ((shmd = find_vma_intersection(current->mm, addr,
21961                              addr + shp->u.shm_segsz))) {
21962       /* printk("shmat() -> EINVAL because the interval "
21963        *        "[0x%lx,0x%lx) intersects an already "
21964        *        "mapped interval [0x%lx,0x%lx).\n",
21965        *        addr, addr + shp->shm_segsz,
21966        *        shmd->vm_start, shmd->vm_end); */
21967       goto out;
21968     }
21969 
21970   err = -EACCES;
21971   if (ipcperms(&shp->u.shm_perm, shmflg & SHM_RDONLY
21972                             ? S_IRUGO : S_IRUGO|S_IWUGO))
21973     goto out;
21974   err = -EIDRM;
21975   if (shp->u.shm_perm.seq !=
21976       (unsigned int) shmid / SHMMNI)
21977     goto out;
21978 
21979   err = -ENOMEM;
21980   shmd = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
21981   if (!shmd)
21982     goto out;
21983   if ((shp != shm_segs[id]) ||
21984       (shp->u.shm_perm.seq !=
21985        (unsigned int) shmid / SHMMNI)) {
21986     kmem_cache_free(vm_area_cachep, shmd);
21987     err = -EIDRM;
21988     goto out;
21989   }
21990 
21991   shmd->vm_pte = SWP_ENTRY(SHM_SWP_TYPE, id);
21992   shmd->vm_start = addr;
21993   shmd->vm_end = addr + shp->shm_npages * PAGE_SIZE;
21994   shmd->vm_mm = current->mm;
21995   shmd->vm_page_prot = (shmflg & SHM_RDONLY)
21996                        ? PAGE_READONLY : PAGE_SHARED;
21997   shmd->vm_flags = VM_SHM | VM_MAYSHARE | VM_SHARED
21998     | VM_MAYREAD | VM_MAYEXEC | VM_READ | VM_EXEC
21999     | ((shmflg & SHM_RDONLY) ? 0 : VM_MAYWRITE|VM_WRITE);
22000   shmd->vm_file = NULL;
22001   shmd->vm_offset = 0;
22002   shmd->vm_ops = &shm_vm_ops;
22003 
22004   shp->u.shm_nattch++;          /* prevent destruction */
22005   if ((err = shm_map (shmd))) {
22006     if (--shp->u.shm_nattch <= 0 &&
22007         shp->u.shm_perm.mode & SHM_DEST)
22008       killseg(id);
22009     kmem_cache_free(vm_area_cachep, shmd);
22010     goto out;
22011   }
22012 
22013   /* insert shmd into shp->attaches */
22014   insert_attach(shp,shmd);
22015 
22016   shp->u.shm_lpid = current->pid;
22017   shp->u.shm_atime = CURRENT_TIME;
22018 
22019   *raddr = addr;
22020   err = 0;
22021 out:
22022   unlock_kernel();
22023   up(&current->mm->mmap_sem);
22024   return err;
22025 }
22026 
22027 /* This is called by fork, once for every shm attach. */
 Комментарий
22028 static void shm_open (struct vm_area_struct *shmd)
22029 {
22030   unsigned int id;
22031   struct shmid_kernel *shp;
22032 
22033   id = SWP_OFFSET(shmd->vm_pte) & SHM_ID_MASK;
22034   shp = shm_segs[id];
22035   if (shp == IPC_UNUSED) {
22036     printk("shm_open: unused id=%d PANIC\n", id);
22037     return;
22038   }
22039   /* insert shmd into shp->attaches */
22040   insert_attach(shp,shmd);
22041   shp->u.shm_nattch++;
22042   shp->u.shm_atime = CURRENT_TIME;
22043   shp->u.shm_lpid = current->pid;
22044 }
22045 
22046 /* remove the attach descriptor shmd.
22047  * free memory for segment if it is marked destroyed.
22048  * The descriptor has already been removed from the
22049  * current->mm->mmap list and will later be kfree()d.  */
 Комментарий
22050 static void shm_close (struct vm_area_struct *shmd)
22051 {
22052   struct shmid_kernel *shp;
22053   int id;
22054 
22055   /* remove from the list of attaches of the shm seg */
22056   id = SWP_OFFSET(shmd->vm_pte) & SHM_ID_MASK;
22057   shp = shm_segs[id];
 Комментарий
22058   remove_attach(shp,shmd); /* remove from shp->attaches*/
22059   shp->u.shm_lpid = current->pid;
22060   shp->u.shm_dtime = CURRENT_TIME;
22061   if (--shp->u.shm_nattch <= 0 &&
22062       shp->u.shm_perm.mode & SHM_DEST)
22063     killseg (id);
22064 }
22065 
22066 /* detach and kill segment if marked destroyed.  The work
22067  * is done in shm_close.  */
22068 asmlinkage int sys_shmdt (char *shmaddr)
22069 {
22070   struct vm_area_struct *shmd, *shmdnext;
22071 
22072   down(&current->mm->mmap_sem);
22073   lock_kernel();
22074   for (shmd = current->mm->mmap; shmd; shmd = shmdnext) {
22075     shmdnext = shmd->vm_next;
22076     if (shmd->vm_ops == &shm_vm_ops &&
22077         shmd->vm_start - shmd->vm_offset ==
22078           (ulong) shmaddr)
22079       do_munmap(shmd->vm_start,
22080                 shmd->vm_end - shmd->vm_start);
22081   }
22082   unlock_kernel();
22083   up(&current->mm->mmap_sem);
22084   return 0;
22085 }
22086 
22087 /* page not present ... go through shm_pages */
22088 static pte_t shm_swap_in(struct vm_area_struct * shmd,
22089   unsigned long offset, unsigned long code)
22090 {
22091   pte_t pte;
22092   struct shmid_kernel *shp;
22093   unsigned int id, idx;
22094 
22095   id = SWP_OFFSET(code) & SHM_ID_MASK;
22096 #ifdef DEBUG_SHM
22097   if (id != (SWP_OFFSET(shmd->vm_pte) & SHM_ID_MASK)) {
22098     printk("shm_swap_in: code id = %d and shmd id = %ld "
22099            "differ\n",
22100            id, SWP_OFFSET(shmd->vm_pte) & SHM_ID_MASK);
22101     return BAD_PAGE;
22102   }
22103   if (id > max_shmid) {
22104     printk("shm_swap_in: id=%d too big. proc mem "
22105            "corrupted\n", id);
22106     return BAD_PAGE;
22107   }
22108 #endif
22109   shp = shm_segs[id];
22110 
22111 #ifdef DEBUG_SHM
22112   if (shp == IPC_UNUSED || shp == IPC_NOID) {
22113     printk ("shm_swap_in: id=%d invalid. Race.\n", id);
22114     return BAD_PAGE;
22115   }
22116 #endif
22117   idx =
22118     (SWP_OFFSET(code) >> SHM_IDX_SHIFT) & SHM_IDX_MASK;
22119 #ifdef DEBUG_SHM
22120   if (idx != (offset >> PAGE_SHIFT)) {
22121     printk("shm_swap_in: code idx = %u and shmd idx = "
22122            "%lu differ\n", idx, offset >> PAGE_SHIFT);
22123     return BAD_PAGE;
22124   }
22125   if (idx >= shp->shm_npages) {
22126     printk("shm_swap_in: too large page index. id=%d\n",
22127            id);
22128     return BAD_PAGE;
22129   }
22130 #endif
22131 
22132   pte = __pte(shp->shm_pages[idx]);
22133   if (!pte_present(pte)) {
22134     unsigned long page = get_free_page(GFP_KERNEL);
22135     if (!page) {
22136       oom(current);
22137       return BAD_PAGE;
22138     }
22139     pte = __pte(shp->shm_pages[idx]);
22140     if (pte_present(pte)) {
22141       free_page (page); /* doesn't sleep */
22142       goto done;
22143     }
22144     if (!pte_none(pte)) {
22145       rw_swap_page_nocache(READ, pte_val(pte),
22146                            (char *)page);
22147       pte = __pte(shp->shm_pages[idx]);
22148       if (pte_present(pte))  {
22149         free_page (page); /* doesn't sleep */
22150         goto done;
22151       }
22152       swap_free(pte_val(pte));
22153       shm_swp--;
22154     }
22155     shm_rss++;
22156     pte = pte_mkdirty(mk_pte(page, PAGE_SHARED));
22157     shp->shm_pages[idx] = pte_val(pte);
22158   } else
22159     --current->maj_flt; /* was incd in do_no_page */
22160 
22161 done:   /* pte_val(pte) == shp->shm_pages[idx] */
22162   current->min_flt++;
22163   atomic_inc(&mem_map[MAP_NR(pte_page(pte))].count);
22164   return pte_modify(pte, shmd->vm_page_prot);
22165 }
22166 
22167 /* Goes through counter = (shm_rss >> prio) present shm
22168  * pages.  */
22169 static unsigned long swap_id = 0; /* now being swapped */
22170 static unsigned long swap_idx = 0; /* next to swap */
22171 
22172 int shm_swap (int prio, int gfp_mask)
22173 {
22174   pte_t page;
22175   struct shmid_kernel *shp;
22176   struct vm_area_struct *shmd;
22177   unsigned long swap_nr;
22178   unsigned long id, idx;
22179   int loop = 0;
22180   int counter;
22181 
22182   counter = shm_rss >> prio;
22183   if (!counter || !(swap_nr = get_swap_page()))
22184     return 0;
22185 
22186  check_id:
22187   shp = shm_segs[swap_id];
22188   if (shp == IPC_UNUSED || shp == IPC_NOID ||
22189       shp->u.shm_perm.mode & SHM_LOCKED ) {
22190     next_id:
22191     swap_idx = 0;
22192     if (++swap_id > max_shmid) {
22193       if (loop)
22194         goto failed;
22195       loop = 1;
22196       swap_id = 0;
22197     }
22198     goto check_id;
22199   }
22200   id = swap_id;
22201 
22202  check_table:
22203   idx = swap_idx++;
22204   if (idx >= shp->shm_npages)
22205     goto next_id;
22206 
22207   page = __pte(shp->shm_pages[idx]);
22208   if (!pte_present(page))
22209     goto check_table;
22210   if ((gfp_mask & __GFP_DMA) &&
22211       !PageDMA(&mem_map[MAP_NR(pte_page(page))]))
22212     goto check_table;
22213   swap_attempts++;
22214 
22215   if (--counter < 0) { /* failed */
22216     failed:
22217     swap_free (swap_nr);
22218     return 0;
22219   }
22220   if (shp->attaches)
22221     for (shmd = shp->attaches; ; ) {
22222       do {
22223     pgd_t *page_dir;
22224     pmd_t *page_middle;
22225     pte_t *page_table, pte;
22226     unsigned long tmp;
22227 
22228     if ((SWP_OFFSET(shmd->vm_pte) & SHM_ID_MASK) != id) {
22229       printk("shm_swap: id=%ld does not match "
22230              "shmd->vm_pte.id=%ld\n",
22231              id, SWP_OFFSET(shmd->vm_pte) & SHM_ID_MASK);
22232       continue;
22233     }
22234     tmp = shmd->vm_start + (idx << PAGE_SHIFT) -
22235       shmd->vm_offset;
22236     if (!(tmp >= shmd->vm_start && tmp < shmd->vm_end))
22237       continue;
22238     page_dir = pgd_offset(shmd->vm_mm,tmp);
22239     if (pgd_none(*page_dir) || pgd_bad(*page_dir)) {
22240       printk("shm_swap: bad pgtbl! id=%ld start=%lx "
22241              "idx=%ld\n", id, shmd->vm_start, idx);
22242       pgd_clear(page_dir);
22243       continue;
22244     }
22245     page_middle = pmd_offset(page_dir,tmp);
22246     if (pmd_none(*page_middle) || pmd_bad(*page_middle)){
22247       printk("shm_swap: bad pgmid! id=%ld start=%lx "
22248              "idx=%ld\n", id, shmd->vm_start, idx);
22249       pmd_clear(page_middle);
22250       continue;
22251     }
22252     page_table = pte_offset(page_middle,tmp);
22253     pte = *page_table;
22254     if (!pte_present(pte))
22255       continue;
22256     if (pte_young(pte)) {
22257       set_pte(page_table, pte_mkold(pte));
22258       continue;
22259     }
22260     if (pte_page(pte) != pte_page(page))
22261       printk("shm_swap_out: page and pte mismatch "
22262              "%lx %lx\n", pte_page(pte),pte_page(page));
22263     flush_cache_page(shmd, tmp);
22264     set_pte(page_table,
22265       __pte(shmd->vm_pte +
22266             SWP_ENTRY(0, idx << SHM_IDX_SHIFT)));
22267     atomic_dec(&mem_map[MAP_NR(pte_page(pte))].count);
22268     if (shmd->vm_mm->rss > 0)
22269       shmd->vm_mm->rss--;
22270     flush_tlb_page(shmd, tmp);
22271       /* continue looping through the linked list */
22272       } while (0);
22273       shmd = shmd->vm_next_share;
22274       if (!shmd)
22275     break;
22276   }
22277 
22278   if (atomic_read(&mem_map[MAP_NR(pte_page(page))].count)
22279       != 1)
22280     goto check_table;
22281   shp->shm_pages[idx] = swap_nr;
22282   rw_swap_page_nocache(WRITE, swap_nr,
22283                        (char *) pte_page(page));
22284   free_page(pte_page(page));
22285   swap_successes++;
22286   shm_swp++;
22287   shm_rss--;
22288   return 1;
22289 }
22290 
22291 /* Free the swap entry and set the new pte for the shm
22292  * page.  */
22293 static void shm_unuse_page(struct shmid_kernel *shp,
22294   unsigned long idx, unsigned long page,
22295   unsigned long entry)
22296 {
22297   pte_t pte;
22298 
22299   pte = pte_mkdirty(mk_pte(page, PAGE_SHARED));
22300   shp->shm_pages[idx] = pte_val(pte);
22301   atomic_inc(&mem_map[MAP_NR(page)].count);
22302   shm_rss++;
22303 
22304   swap_free(entry);
22305   shm_swp--;
22306 }
22307 
22308 /* unuse_shm() search for an eventually swapped out shm
22309  * page.  */
22310 void shm_unuse(unsigned long entry, unsigned long page)
22311 {
22312   int i, n;
22313 
22314   for (i = 0; i < SHMMNI; i++)
22315     if (shm_segs[i] != IPC_UNUSED &&
22316         shm_segs[i] != IPC_NOID)
22317       for (n = 0; n < shm_segs[i]->shm_npages; n++)
22318         if (shm_segs[i]->shm_pages[n] == entry)
22319         {
22320           shm_unuse_page(shm_segs[i], n,
22321                    page, entry);
22322           return;
22323         }
22324 }

netlib.narod.ru< Назад | Оглавление | Далее >

Сайт управляется системой uCoz