4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 * Copyright (c) 2015 Joyent, Inc.
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
31 #include <sys/types.h>
32 #include <sys/bitmap.h>
33 #include <sys/sysmacros.h>
35 #include <sys/param.h>
36 #include <sys/systm.h>
38 #include <sys/unistd.h>
39 #include <sys/errno.h>
42 #include <sys/tuneable.h>
43 #include <sys/cmn_err.h>
45 #include <sys/vmsystm.h>
46 #include <sys/debug.h>
47 #include <sys/policy.h>
52 static uint_t
mem_getpgszc(size_t);
55 * Memory control operations
58 memcntl(caddr_t addr
, size_t len
, int cmd
, caddr_t arg
, int attr
, int mask
)
60 struct as
*as
= ttoproc(curthread
)->p_as
;
61 struct proc
*p
= ttoproc(curthread
);
63 uint_t szc
, oszc
, pgcmd
;
67 STRUCT_DECL(memcntl_mha
, mha
);
70 return (set_errno(EINVAL
));
71 if ((cmd
== MC_LOCKAS
) || (cmd
== MC_UNLOCKAS
)) {
72 if ((addr
!= 0) || (len
!= 0)) {
73 return (set_errno(EINVAL
));
75 } else if (cmd
!= MC_HAT_ADVISE
) {
76 if (((uintptr_t)addr
& PAGEOFFSET
) != 0 || len
== 0) {
77 return (set_errno(EINVAL
));
80 * We're only concerned with the address range
81 * here, not the protections. The protections
82 * are only used as a "filter" in this code,
83 * they aren't set or modified here.
85 if (valid_usr_range(addr
, len
, 0, as
,
86 as
->a_userlimit
) != RANGE_OKAY
) {
87 return (set_errno(ENOMEM
));
91 if (cmd
== MC_HAT_ADVISE
) {
92 if (attr
!= 0 || mask
!= 0) {
93 return (set_errno(EINVAL
));
97 if ((VALID_ATTR
& attr
) != attr
) {
98 return (set_errno(EINVAL
));
100 if ((attr
& SHARED
) && (attr
& PRIVATE
)) {
101 return (set_errno(EINVAL
));
103 if (((cmd
== MC_LOCKAS
) || (cmd
== MC_LOCK
) ||
104 (cmd
== MC_UNLOCKAS
) || (cmd
== MC_UNLOCK
)) &&
105 (error
= secpolicy_lock_memory(CRED())) != 0)
106 return (set_errno(error
));
115 * MS_SYNC used to be defined to be zero but is now non-zero.
116 * For binary compatibility we still accept zero
117 * (the absence of MS_ASYNC) to mean the same thing.
119 iarg
= (uintptr_t)arg
;
120 if ((iarg
& ~MS_INVALIDATE
) == 0)
123 if (((iarg
& ~(MS_SYNC
|MS_ASYNC
|MS_INVALIDATE
)) != 0) ||
124 ((iarg
& (MS_SYNC
|MS_ASYNC
)) == (MS_SYNC
|MS_ASYNC
))) {
125 error
= set_errno(EINVAL
);
127 error
= as_ctl(as
, addr
, len
, cmd
, attr
, iarg
, NULL
, 0);
129 (void) set_errno(error
);
134 if ((uintptr_t)arg
& ~(MCL_FUTURE
|MCL_CURRENT
) ||
135 (uintptr_t)arg
== 0) {
136 return (set_errno(EINVAL
));
145 * Set prefered page size.
147 STRUCT_INIT(mha
, get_udatamodel());
148 if (copyin(arg
, STRUCT_BUF(mha
), STRUCT_SIZE(mha
))) {
149 return (set_errno(EFAULT
));
152 pgcmd
= STRUCT_FGET(mha
, mha_cmd
);
155 * Currently only MHA_MAPSIZE_VA, MHA_MAPSIZE_STACK
156 * and MHA_MAPSIZE_BSSBRK are supported. Only one
157 * command may be specified at a time.
159 if ((~(MHA_MAPSIZE_VA
|MHA_MAPSIZE_STACK
|MHA_MAPSIZE_BSSBRK
) &
160 pgcmd
) || pgcmd
== 0 || !ISP2(pgcmd
) ||
161 STRUCT_FGET(mha
, mha_flags
))
162 return (set_errno(EINVAL
));
164 pgsz
= STRUCT_FGET(mha
, mha_pagesize
);
167 * call platform specific map_pgsz() routine to get the
168 * optimal pgsz if pgsz is 0.
170 * For stack and heap operations addr and len must be zero.
172 if ((pgcmd
& (MHA_MAPSIZE_BSSBRK
|MHA_MAPSIZE_STACK
)) != 0) {
173 if (addr
!= NULL
|| len
!= 0) {
174 return (set_errno(EINVAL
));
178 * Disable autompss for this process unless pgsz == 0,
179 * which means the system should pick. In the
180 * pgsz == 0 case, leave the SAUTOLPG setting alone, as
181 * we don't want to enable it when someone has
182 * disabled automatic large page selection for the
185 mutex_enter(&p
->p_lock
);
187 p
->p_flag
&= ~SAUTOLPG
;
189 mutex_exit(&p
->p_lock
);
196 if (pgcmd
== MHA_MAPSIZE_BSSBRK
)
201 pgsz
= map_pgsz(type
, p
, 0, 0, 1);
205 * addr and len must be valid for range specified.
207 if (valid_usr_range(addr
, len
, 0, as
,
208 as
->a_userlimit
) != RANGE_OKAY
) {
209 return (set_errno(ENOMEM
));
212 * Note that we don't disable automatic large page
213 * selection for anon segments based on use of
217 error
= as_set_default_lpsize(as
, addr
, len
);
219 (void) set_errno(error
);
225 * addr and len must be prefered page size aligned
227 if (!IS_P2ALIGNED(addr
, pgsz
) ||
228 !IS_P2ALIGNED(len
, pgsz
)) {
229 return (set_errno(EINVAL
));
233 szc
= mem_getpgszc(pgsz
);
234 if (szc
== (uint_t
)-1) {
235 if ((pgcmd
& (MHA_MAPSIZE_BSSBRK
|MHA_MAPSIZE_STACK
))
239 return (set_errno(EINVAL
));
243 * For stack and heap operations we first need to pad
244 * out existing range (create new mappings) to the new
245 * prefered page size boundary. Also the start of the
246 * .bss for the heap or user's stack base may not be on
247 * the new prefered page size boundary. For these cases
248 * we align the base of the request on the new prefered
251 if (pgcmd
& MHA_MAPSIZE_BSSBRK
) {
252 if (szc
== p
->p_brkpageszc
) {
256 if (szc
> p
->p_brkpageszc
) {
257 error
= brk_internal(p
->p_brkbase
258 + p
->p_brksize
, szc
);
261 return (set_errno(error
));
265 * It is possible for brk_internal to silently fail to
266 * promote the heap size, so don't panic or ASSERT.
268 if (!IS_P2ALIGNED(p
->p_brkbase
+ p
->p_brksize
, pgsz
)) {
270 return (set_errno(ENOMEM
));
272 oszc
= p
->p_brkpageszc
;
273 p
->p_brkpageszc
= szc
;
275 addr
= (caddr_t
)P2ROUNDUP((uintptr_t)p
->p_bssbase
,
277 len
= (p
->p_brkbase
+ p
->p_brksize
) - addr
;
278 ASSERT(IS_P2ALIGNED(len
, pgsz
));
280 * Perhaps no existing pages to promote.
288 * The code below, as does grow.c, assumes stacks always grow
291 if (pgcmd
& MHA_MAPSIZE_STACK
) {
292 if (szc
== p
->p_stkpageszc
) {
297 if (szc
> p
->p_stkpageszc
) {
298 error
= grow_internal(p
->p_usrstack
-
302 return (set_errno(error
));
306 * It is possible for grow_internal to silently fail to
307 * promote the stack size, so don't panic or ASSERT.
309 if (!IS_P2ALIGNED(p
->p_usrstack
- p
->p_stksize
, pgsz
)) {
311 return (set_errno(ENOMEM
));
313 oszc
= p
->p_stkpageszc
;
314 p
->p_stkpageszc
= szc
;
316 addr
= p
->p_usrstack
- p
->p_stksize
;
317 len
= P2ALIGN(p
->p_stksize
, pgsz
);
320 * Perhaps nothing to promote.
322 if (len
== 0 || addr
>= p
->p_usrstack
||
323 (addr
+ len
) < addr
) {
328 ASSERT(IS_P2ALIGNED(addr
, pgsz
));
329 ASSERT(IS_P2ALIGNED(len
, pgsz
));
330 error
= as_setpagesize(as
, addr
, len
, szc
, B_TRUE
);
333 * On stack or heap failures restore original
337 if ((pgcmd
& MHA_MAPSIZE_BSSBRK
) != 0) {
338 p
->p_brkpageszc
= oszc
;
340 if ((pgcmd
& MHA_MAPSIZE_STACK
) != 0) {
341 p
->p_stkpageszc
= oszc
;
343 (void) set_errno(error
);
345 if ((pgcmd
& (MHA_MAPSIZE_BSSBRK
|MHA_MAPSIZE_STACK
)) != 0) {
350 if ((uintptr_t)arg
== MADV_FREE
||
351 (uintptr_t)arg
== MADV_PURGE
) {
354 switch ((uintptr_t)arg
) {
356 fc
= as_faulta(as
, addr
, len
);
358 if (FC_CODE(fc
) == FC_OBJERR
)
359 error
= set_errno(FC_ERRNO(fc
));
360 else if (FC_CODE(fc
) == FC_NOMAP
)
361 error
= set_errno(ENOMEM
);
363 error
= set_errno(EINVAL
);
370 * For now, don't need is turned into an as_ctl(MC_SYNC)
371 * operation flagged for async invalidate.
373 error
= as_ctl(as
, addr
, len
, MC_SYNC
, attr
,
374 MS_ASYNC
| MS_INVALIDATE
, NULL
, 0);
376 (void) set_errno(error
);
380 error
= as_ctl(as
, addr
, len
, cmd
, attr
,
381 (uintptr_t)arg
, NULL
, 0);
383 (void) set_errno(error
);
387 case MC_INHERIT_ZERO
:
388 if (arg
!= 0 || attr
!= 0 || mask
!= 0)
389 return (set_errno(EINVAL
));
392 return (set_errno(EINVAL
));
395 error
= as_ctl(as
, addr
, len
, cmd
, attr
, (uintptr_t)arg
, NULL
, 0);
398 (void) set_errno(error
);
403 * Return page size code for page size passed in. If
404 * matching page size not found or supported, return -1.
407 mem_getpgszc(size_t pgsz
) {
408 return ((uint_t
)page_szc_user_filtered(pgsz
));