diff options
Diffstat (limited to 'usr/src/uts/common/os/shm.c')
-rw-r--r-- | usr/src/uts/common/os/shm.c | 188 |
1 files changed, 72 insertions, 116 deletions
diff --git a/usr/src/uts/common/os/shm.c b/usr/src/uts/common/os/shm.c index b8038fd0ae..5c03ab7803 100644 --- a/usr/src/uts/common/os/shm.c +++ b/usr/src/uts/common/os/shm.c @@ -108,6 +108,7 @@ #include <sys/project.h> #include <sys/policy.h> #include <sys/zone.h> +#include <sys/rctl.h> #include <sys/ipc.h> #include <sys/ipc_impl.h> @@ -125,11 +126,11 @@ #include <c2/audit.h> -static int shmem_lock(struct anon_map *amp); -static void shmem_unlock(struct anon_map *amp, uint_t lck); +static int shmem_lock(kshmid_t *sp, struct anon_map *amp); +static void shmem_unlock(kshmid_t *sp, struct anon_map *amp); static void sa_add(struct proc *pp, caddr_t addr, size_t len, ulong_t flags, kshmid_t *id); -static void shm_rm_amp(struct anon_map *amp, uint_t lckflag); +static void shm_rm_amp(struct anon_map *amp); static void shm_dtor(kipc_perm_t *); static void shm_rmid(kipc_perm_t *); static void shm_remove_zone(zoneid_t, void *); @@ -464,7 +465,6 @@ shmat(int shmid, caddr_t uaddr, int uflags, uintptr_t *rvp) sp->shm_sptinfo->sptas = segspt->s_as; sp->shm_sptseg = segspt; sp->shm_sptprot = prot; - sp->shm_lkcnt = 0; } else if ((prot & sp->shm_sptprot) != sp->shm_sptprot) { /* * Ensure we're attaching to an ISM segment with @@ -573,6 +573,11 @@ shm_dtor(kipc_perm_t *perm) uint_t cnt; size_t rsize; + if (sp->shm_lkcnt > 0) { + shmem_unlock(sp, sp->shm_amp); + sp->shm_lkcnt = 0; + } + if (sp->shm_sptinfo) { if (isspt(sp)) sptdestroy(sp->shm_sptinfo->sptas, sp->shm_amp); @@ -583,7 +588,7 @@ shm_dtor(kipc_perm_t *perm) cnt = --sp->shm_amp->refcnt; ANON_LOCK_EXIT(&sp->shm_amp->a_rwlock); ASSERT(cnt == 0); - shm_rm_amp(sp->shm_amp, sp->shm_lkcnt); + shm_rm_amp(sp->shm_amp); if (sp->shm_perm.ipc_id != IPC_ID_INVAL) { rsize = ptob(btopr(sp->shm_segsz)); @@ -705,8 +710,13 @@ shmctl(int shmid, int cmd, void *arg) if ((error = secpolicy_lock_memory(cr)) != 0) break; + /* protect against overflow */ + if (sp->shm_lkcnt >= USHRT_MAX) { + error = ENOMEM; + break; + } if (!isspt(sp) && (sp->shm_lkcnt++ == 0)) { - if (error = shmem_lock(sp->shm_amp)) { + if (error = shmem_lock(sp, sp->shm_amp)) { ANON_LOCK_ENTER(&sp->shm_amp->a_rwlock, RW_WRITER); cmn_err(CE_NOTE, "shmctl - couldn't lock %ld pages into memory", @@ -714,7 +724,6 @@ shmctl(int shmid, int cmd, void *arg) ANON_LOCK_EXIT(&sp->shm_amp->a_rwlock); error = ENOMEM; sp->shm_lkcnt--; - shmem_unlock(sp->shm_amp, 0); } } break; @@ -724,10 +733,8 @@ shmctl(int shmid, int cmd, void *arg) if ((error = secpolicy_lock_memory(cr)) != 0) break; - if (!isspt(sp)) { - if (sp->shm_lkcnt && (--sp->shm_lkcnt == 0)) { - shmem_unlock(sp->shm_amp, 1); - } + if (sp->shm_lkcnt && (--sp->shm_lkcnt == 0)) { + shmem_unlock(sp, sp->shm_amp); } break; @@ -863,7 +870,7 @@ top: } sp->shm_amp = anonmap_alloc(rsize, rsize); - + sp->shm_amp->a_sp = sp; /* * Store the original user's requested size, in bytes, * rather than the page-aligned size. The former is @@ -878,7 +885,6 @@ top: sp->shm_cpid = curproc->p_pid; sp->shm_ismattch = 0; sp->shm_sptinfo = NULL; - /* * Check limits one last time, push id into global * visibility, and update resource usage counts. @@ -1094,115 +1100,58 @@ shmexit(struct proc *pp) * At this time pages should be in memory, so just lock them. */ static void -lock_again(size_t npages, struct anon_map *amp) +lock_again(size_t npages, kshmid_t *sp, struct anon_map *amp) { struct anon *ap; struct page *pp; struct vnode *vp; - anoff_t off; + u_offset_t off; ulong_t anon_idx; anon_sync_obj_t cookie; + mutex_enter(&sp->shm_mlock); ANON_LOCK_ENTER(&->a_rwlock, RW_READER); - for (anon_idx = 0; npages != 0; anon_idx++, npages--) { anon_array_enter(amp, anon_idx, &cookie); ap = anon_get_ptr(amp->ahp, anon_idx); + ASSERT(ap != NULL); swap_xlate(ap, &vp, &off); anon_array_exit(&cookie); - pp = page_lookup(vp, (u_offset_t)off, SE_SHARED); + pp = page_lookup(vp, off, SE_SHARED); if (pp == NULL) { panic("lock_again: page not in the system"); /*NOTREACHED*/ } + /* page should already be locked by caller */ + ASSERT(pp->p_lckcnt > 0); (void) page_pp_lock(pp, 0, 0); page_unlock(pp); } ANON_LOCK_EXIT(&->a_rwlock); + mutex_exit(&sp->shm_mlock); } -/* check if this segment is already locked. */ -/*ARGSUSED*/ -static int -check_locked(struct as *as, struct segvn_data *svd, size_t npages) -{ - struct vpage *vpp = svd->vpage; - size_t i; - if (svd->vpage == NULL) - return (0); /* unlocked */ - - SEGVN_LOCK_ENTER(as, &svd->lock, RW_READER); - for (i = 0; i < npages; i++, vpp++) { - if (VPP_ISPPLOCK(vpp) == 0) { - SEGVN_LOCK_EXIT(as, &svd->lock); - return (1); /* partially locked */ - } - } - SEGVN_LOCK_EXIT(as, &svd->lock); - return (2); /* locked */ -} - - /* * Attach the shared memory segment to the process * address space and lock the pages. */ static int -shmem_lock(struct anon_map *amp) +shmem_lock(kshmid_t *sp, struct anon_map *amp) { size_t npages = btopr(amp->size); - struct seg *seg; struct as *as; struct segvn_crargs crargs; - struct segvn_data *svd; - proc_t *p = curproc; - caddr_t addr; - uint_t error, ret; - caddr_t seg_base; - size_t seg_sz; - - as = p->p_as; - AS_LOCK_ENTER(as, &as->a_lock, RW_READER); - /* check if shared memory is already attached */ - for (seg = AS_SEGFIRST(as); seg != NULL; seg = AS_SEGNEXT(as, seg)) { - svd = (struct segvn_data *)seg->s_data; - if ((seg->s_ops == &segvn_ops) && (svd->amp == amp) && - (amp->size == seg->s_size)) { - switch (ret = check_locked(as, svd, npages)) { - case 0: /* unlocked */ - case 1: /* partially locked */ - seg_base = seg->s_base; - seg_sz = seg->s_size; - - AS_LOCK_EXIT(as, &as->a_lock); - if ((error = as_ctl(as, seg_base, seg_sz, - MC_LOCK, 0, 0, NULL, 0)) == 0) - lock_again(npages, amp); - (void) as_ctl(as, seg_base, seg_sz, MC_UNLOCK, - 0, 0, NULL, NULL); - return (error); - case 2: /* locked */ - AS_LOCK_EXIT(as, &as->a_lock); - lock_again(npages, amp); - return (0); - default: - cmn_err(CE_WARN, "shmem_lock: deflt %d", ret); - break; - } - } - } - AS_LOCK_EXIT(as, &as->a_lock); + uint_t error; - /* attach shm segment to our address space */ - as_rangelock(as); - map_addr(&addr, amp->size, 0ll, 1, 0); - if (addr == NULL) { - as_rangeunlock(as); - return (ENOMEM); - } + /* + * A later ISM/DISM attach may increase the size of the amp, so + * cache the number of pages locked for the future shmem_unlock() + */ + sp->shm_lkpages = npages; + as = as_alloc(); /* Initialize the create arguments and map the segment */ crargs = *(struct segvn_crargs *)zfod_argsp; /* structure copy */ crargs.offset = (u_offset_t)0; @@ -1211,16 +1160,15 @@ shmem_lock(struct anon_map *amp) crargs.prot = PROT_ALL; crargs.maxprot = crargs.prot; crargs.flags = 0; - - error = as_map(as, addr, amp->size, segvn_create, &crargs); - as_rangeunlock(as); + error = as_map(as, 0x0, amp->size, segvn_create, &crargs); if (!error) { - if ((error = as_ctl(as, addr, amp->size, MC_LOCK, 0, 0, + if ((error = as_ctl(as, 0x0, amp->size, MC_LOCK, 0, 0, NULL, 0)) == 0) { - lock_again(npages, amp); + lock_again(npages, sp, amp); } - (void) as_unmap(as, addr, amp->size); + (void) as_unmap(as, 0x0, amp->size); } + as_free(as); return (error); } @@ -1229,38 +1177,53 @@ shmem_lock(struct anon_map *amp) * Unlock shared memory */ static void -shmem_unlock(struct anon_map *amp, uint_t lck) +shmem_unlock(kshmid_t *sp, struct anon_map *amp) { struct anon *ap; - pgcnt_t npages = btopr(amp->size); + pgcnt_t npages = sp->shm_lkpages; struct vnode *vp; struct page *pp; - anoff_t off; + u_offset_t off; ulong_t anon_idx; + size_t unlocked_bytes = 0; + kproject_t *proj; + anon_sync_obj_t cookie; + proj = sp->shm_perm.ipc_proj; + mutex_enter(&sp->shm_mlock); + ANON_LOCK_ENTER(&->a_rwlock, RW_READER); for (anon_idx = 0; anon_idx < npages; anon_idx++) { + anon_array_enter(amp, anon_idx, &cookie); if ((ap = anon_get_ptr(amp->ahp, anon_idx)) == NULL) { - if (lck) { - panic("shmem_unlock: null app"); - /*NOTREACHED*/ - } - continue; + panic("shmem_unlock: null app"); + /*NOTREACHED*/ } swap_xlate(ap, &vp, &off); + anon_array_exit(&cookie); pp = page_lookup(vp, off, SE_SHARED); if (pp == NULL) { - if (lck) { - panic("shmem_unlock: page not in the system"); - /*NOTREACHED*/ - } - continue; - } - if (pp->p_lckcnt) { - page_pp_unlock(pp, 0, 0); + panic("shmem_unlock: page not in the system"); + /*NOTREACHED*/ } + /* + * Page should at least have once lock from previous + * shmem_lock + */ + ASSERT(pp->p_lckcnt > 0); + page_pp_unlock(pp, 0, 0); + if (pp->p_lckcnt == 0) + unlocked_bytes += PAGESIZE; + page_unlock(pp); } + + if (unlocked_bytes > 0) { + rctl_decr_locked_mem(NULL, proj, unlocked_bytes, 0); + } + + ANON_LOCK_EXIT(&->a_rwlock); + mutex_exit(&sp->shm_mlock); } /* @@ -1268,16 +1231,9 @@ shmem_unlock(struct anon_map *amp, uint_t lck) * amp. This means all shmdt()s and the IPC_RMID have been done. */ static void -shm_rm_amp(struct anon_map *amp, uint_t lckflag) +shm_rm_amp(struct anon_map *amp) { /* - * If we are finally deleting the - * shared memory, and if no one did - * the SHM_UNLOCK, we must do it now. - */ - shmem_unlock(amp, lckflag); - - /* * Free up the anon_map. */ lgrp_shm_policy_fini(amp, NULL); |