Ядро Linux в комментариях

       

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 }


Содержание раздела