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(¤t->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(¤t->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(¤t->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(¤t->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(¤t->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(¤t->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 }
Сайт управляется системой
uCoz