int sys_mlock(struct lwp *l, const struct sys_mlock_args *uap, register_t *retval) { /* { syscallarg(const void *) addr; syscallarg(size_t) len; } */ struct proc *p = l->l_proc; vaddr_t addr; vsize_t size, pageoff; int error; /* * extract syscall args from uap */ addr = (vaddr_t)SCARG(uap, addr); size = (vsize_t)SCARG(uap, len); /* * align the address to a page boundary and adjust the size accordingly */ pageoff = (addr & PAGE_MASK); addr -= pageoff; size += pageoff; size = (vsize_t)round_page(size); error = range_test(&p->p_vmspace->vm_map, addr, size, false); if (error) return ENOMEM; if (atop(size) + uvmexp.wired > uvmexp.wiredmax) return EAGAIN; if (size + ptoa(pmap_wired_count(vm_map_pmap(&p->p_vmspace->vm_map))) > p->p_rlimit[RLIMIT_MEMLOCK].rlim_cur) return EAGAIN; error = uvm_map_pageable(&p->p_vmspace->vm_map, addr, addr+size, false, 0); if (error == EFAULT) error = ENOMEM; return error; }
/* * mlock system call handler * * mlock_args(const void *addr, size_t len) * * No requirements */ int sys_mlock(struct mlock_args *uap) { vm_offset_t addr; vm_offset_t tmpaddr; vm_size_t size, pageoff; struct thread *td = curthread; struct proc *p = td->td_proc; int error; addr = (vm_offset_t) uap->addr; size = uap->len; pageoff = (addr & PAGE_MASK); addr -= pageoff; size += pageoff; size = (vm_size_t) round_page(size); if (size < uap->len) /* wrap */ return(EINVAL); tmpaddr = addr + size; /* workaround gcc4 opt */ if (tmpaddr < addr) /* wrap */ return (EINVAL); if (atop(size) + vmstats.v_wire_count > vm_page_max_wired) return (EAGAIN); /* * We do not need to synchronize against other threads updating ucred; * they update p->ucred, which is synchronized into td_ucred ourselves. */ #ifdef pmap_wired_count if (size + ptoa(pmap_wired_count(vm_map_pmap(&p->p_vmspace->vm_map))) > p->p_rlimit[RLIMIT_MEMLOCK].rlim_cur) { return (ENOMEM); } #else error = priv_check_cred(td->td_ucred, PRIV_ROOT, 0); if (error) { return (error); } #endif error = vm_map_unwire(&p->p_vmspace->vm_map, addr, addr + size, FALSE); return (error == KERN_SUCCESS ? 0 : ENOMEM); }
int sys_mlock(struct proc *p, void *v, register_t *retval) { struct sys_mlock_args /* { syscallarg(const void *) addr; syscallarg(size_t) len; } */ *uap = v; vaddr_t addr; vsize_t size, pageoff; int error; /* * extract syscall args from uap */ addr = (vaddr_t)SCARG(uap, addr); size = (vsize_t)SCARG(uap, len); /* * align the address to a page boundary and adjust the size accordingly */ ALIGN_ADDR(addr, size, pageoff); if (addr > SIZE_MAX - size) return (EINVAL); /* disallow wrap-around. */ if (atop(size) + uvmexp.wired > uvmexp.wiredmax) return (EAGAIN); #ifdef pmap_wired_count if (size + ptoa(pmap_wired_count(vm_map_pmap(&p->p_vmspace->vm_map))) > p->p_rlimit[RLIMIT_MEMLOCK].rlim_cur) return (EAGAIN); #else if ((error = suser(p, 0)) != 0) return (error); #endif error = uvm_map_pageable(&p->p_vmspace->vm_map, addr, addr+size, FALSE, 0); return (error == 0 ? 0 : ENOMEM); }
int uvm_mmap(struct vm_map *map, vaddr_t *addr, vsize_t size, vm_prot_t prot, vm_prot_t maxprot, int flags, int advice, struct uvm_object *uobj, voff_t foff, vsize_t locklimit) { vaddr_t align = 0; int error; uvm_flag_t uvmflag = 0; /* * check params */ if (size == 0) return 0; if (foff & PAGE_MASK) return EINVAL; if ((prot & maxprot) != prot) return EINVAL; /* * for non-fixed mappings, round off the suggested address. * for fixed mappings, check alignment and zap old mappings. */ if ((flags & MAP_FIXED) == 0) { *addr = round_page(*addr); } else { if (*addr & PAGE_MASK) return EINVAL; uvmflag |= UVM_FLAG_FIXED; (void) uvm_unmap(map, *addr, *addr + size); } /* * Try to see if any requested alignment can even be attemped. * Make sure we can express the alignment (asking for a >= 4GB * alignment on an ILP32 architecure make no sense) and the * alignment is at least for a page sized quanitiy. If the * request was for a fixed mapping, make sure supplied address * adheres to the request alignment. */ align = (flags & MAP_ALIGNMENT_MASK) >> MAP_ALIGNMENT_SHIFT; if (align) { if (align >= sizeof(vaddr_t) * NBBY) return EINVAL; align = 1L << align; if (align < PAGE_SIZE) return EINVAL; if (align >= vm_map_max(map)) return ENOMEM; if (flags & MAP_FIXED) { if ((*addr & (align-1)) != 0) return EINVAL; align = 0; } } /* * check resource limits */ if (!VM_MAP_IS_KERNEL(map) && (((rlim_t)curproc->p_vmspace->vm_map.size + (rlim_t)size) > curproc->p_rlimit[RLIMIT_AS].rlim_cur)) return ENOMEM; /* * handle anon vs. non-anon mappings. for non-anon mappings attach * to underlying vm object. */ if (flags & MAP_ANON) { KASSERT(uobj == NULL); foff = UVM_UNKNOWN_OFFSET; if ((flags & MAP_SHARED) == 0) /* XXX: defer amap create */ uvmflag |= UVM_FLAG_COPYONW; else /* shared: create amap now */ uvmflag |= UVM_FLAG_OVERLAY; } else { KASSERT(uobj != NULL); if ((flags & MAP_SHARED) == 0) { uvmflag |= UVM_FLAG_COPYONW; } } uvmflag = UVM_MAPFLAG(prot, maxprot, (flags & MAP_SHARED) ? UVM_INH_SHARE : UVM_INH_COPY, advice, uvmflag); error = uvm_map(map, addr, size, uobj, foff, align, uvmflag); if (error) { if (uobj) uobj->pgops->pgo_detach(uobj); return error; } /* * POSIX 1003.1b -- if our address space was configured * to lock all future mappings, wire the one we just made. * * Also handle the MAP_WIRED flag here. */ if (prot == VM_PROT_NONE) { /* * No more work to do in this case. */ return 0; } if ((flags & MAP_WIRED) != 0 || (map->flags & VM_MAP_WIREFUTURE) != 0) { vm_map_lock(map); if (atop(size) + uvmexp.wired > uvmexp.wiredmax || (locklimit != 0 && size + ptoa(pmap_wired_count(vm_map_pmap(map))) > locklimit)) { vm_map_unlock(map); uvm_unmap(map, *addr, *addr + size); return ENOMEM; } /* * uvm_map_pageable() always returns the map unlocked. */ error = uvm_map_pageable(map, *addr, *addr + size, false, UVM_LK_ENTER); if (error) { uvm_unmap(map, *addr, *addr + size); return error; } return 0; } return 0; }
int uvm_mmap(vm_map_t map, vaddr_t *addr, vsize_t size, vm_prot_t prot, vm_prot_t maxprot, int flags, caddr_t handle, voff_t foff, vsize_t locklimit, struct proc *p) { struct uvm_object *uobj; struct vnode *vp; int error; int advice = UVM_ADV_NORMAL; uvm_flag_t uvmflag = 0; vsize_t align = 0; /* userland page size */ /* * check params */ if (size == 0) return(0); if (foff & PAGE_MASK) return(EINVAL); if ((prot & maxprot) != prot) return(EINVAL); /* * for non-fixed mappings, round off the suggested address. * for fixed mappings, check alignment and zap old mappings. */ if ((flags & MAP_FIXED) == 0) { *addr = round_page(*addr); /* round */ } else { if (*addr & PAGE_MASK) return(EINVAL); uvmflag |= UVM_FLAG_FIXED; uvm_unmap_p(map, *addr, *addr + size, p); /* zap! */ } /* * handle anon vs. non-anon mappings. for non-anon mappings attach * to underlying vm object. */ if (flags & MAP_ANON) { if ((flags & MAP_FIXED) == 0 && size >= __LDPGSZ) align = __LDPGSZ; foff = UVM_UNKNOWN_OFFSET; uobj = NULL; if ((flags & MAP_SHARED) == 0) /* XXX: defer amap create */ uvmflag |= UVM_FLAG_COPYONW; else /* shared: create amap now */ uvmflag |= UVM_FLAG_OVERLAY; } else { vp = (struct vnode *) handle; /* get vnode */ if (vp->v_type != VCHR) { uobj = uvn_attach((void *) vp, (flags & MAP_SHARED) ? maxprot : (maxprot & ~VM_PROT_WRITE)); if (uobj) { assert((void*)uobj == vp); if (flags & MAP_DENYWRITE) uvmflag |= UVM_FLAG_DENYWRITE; if ((flags & MAP_SHARED) && (maxprot & VM_PROT_WRITE)) uvmflag |= UVM_FLAG_WRITECOUNT; } #ifndef UBC /* * XXXCDC: hack from old code * don't allow vnodes which have been mapped * shared-writeable to persist [forces them to be * flushed out when last reference goes]. * XXXCDC: interesting side effect: avoids a bug. * note that in WRITE [ufs_readwrite.c] that we * allocate buffer, uncache, and then do the write. * the problem with this is that if the uncache causes * VM data to be flushed to the same area of the file * we are writing to... in that case we've got the * buffer locked and our process goes to sleep forever. * * XXXCDC: checking maxprot protects us from the * "persistbug" program but this is not a long term * solution. * * XXXCDC: we don't bother calling uncache with the vp * VOP_LOCKed since we know that we are already * holding a valid reference to the uvn (from the * uvn_attach above), and thus it is impossible for * the uncache to kill the uvn and trigger I/O. */ if (flags & MAP_SHARED) { if ((prot & VM_PROT_WRITE) || (maxprot & VM_PROT_WRITE)) { uvm_vnp_uncache(vp); } } #else /* XXX for now, attach doesn't gain a ref */ VREF(vp); #endif } else { uobj = udv_attach((void *) &vp->v_rdev, (flags & MAP_SHARED) ? maxprot : (maxprot & ~VM_PROT_WRITE), foff, size); /* * XXX Some devices don't like to be mapped with * XXX PROT_EXEC, but we don't really have a * XXX better way of handling this, right now */ if (uobj == NULL && (prot & PROT_EXEC) == 0) { maxprot &= ~VM_PROT_EXECUTE; uobj = udv_attach((void *) &vp->v_rdev, (flags & MAP_SHARED) ? maxprot : (maxprot & ~VM_PROT_WRITE), foff, size); } advice = UVM_ADV_RANDOM; } if (uobj == NULL) return((vp->v_type == VREG) ? ENOMEM : EINVAL); if ((flags & MAP_SHARED) == 0) uvmflag |= UVM_FLAG_COPYONW; } /* * set up mapping flags */ uvmflag = UVM_MAPFLAG(prot, maxprot, (flags & MAP_SHARED) ? UVM_INH_SHARE : UVM_INH_COPY, advice, uvmflag); error = uvm_map_p(map, addr, size, uobj, foff, align, uvmflag, p); if (error == 0) { /* * POSIX 1003.1b -- if our address space was configured * to lock all future mappings, wire the one we just made. */ if (prot == VM_PROT_NONE) { /* * No more work to do in this case. */ return (0); } vm_map_lock(map); if (map->flags & VM_MAP_WIREFUTURE) { if ((atop(size) + uvmexp.wired) > uvmexp.wiredmax #ifdef pmap_wired_count || (locklimit != 0 && (size + ptoa(pmap_wired_count(vm_map_pmap(map)))) > locklimit) #endif ) { error = ENOMEM; vm_map_unlock(map); /* unmap the region! */ uvm_unmap(map, *addr, *addr + size); goto bad; } /* * uvm_map_pageable() always returns the map * unlocked. */ error = uvm_map_pageable(map, *addr, *addr + size, FALSE, UVM_LK_ENTER); if (error != 0) { /* unmap the region! */ uvm_unmap(map, *addr, *addr + size); goto bad; } return (0); } vm_map_unlock(map); return (0); } /* * errors: first detach from the uobj, if any. */ if (uobj) uobj->pgops->pgo_detach(uobj); bad: return (error); }