xref: /illumos-gate/usr/src/uts/common/vm/seg_hole.c (revision 45ede40b2394db7967e59f19288fae9b62efd4aa)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2018 Joyent, Inc.
14  */
15 
16 
17 #include <sys/types.h>
18 #include <sys/param.h>
19 #include <sys/errno.h>
20 #include <sys/cred.h>
21 #include <sys/kmem.h>
22 #include <sys/lgrp.h>
23 #include <sys/mman.h>
24 
25 #include <vm/hat.h>
26 #include <vm/as.h>
27 #include <vm/seg.h>
28 #include <vm/seg_hole.h>
29 
30 
31 static int seghole_dup(struct seg *, struct seg *);
32 static int seghole_unmap(struct seg *, caddr_t, size_t);
33 static void seghole_free(struct seg *);
34 static faultcode_t seghole_fault(struct hat *, struct seg *, caddr_t, size_t,
35     enum fault_type, enum seg_rw);
36 static faultcode_t seghole_faulta(struct seg *, caddr_t);
37 static int seghole_setprot(struct seg *, caddr_t, size_t, uint_t);
38 static int seghole_checkprot(struct seg *, caddr_t, size_t, uint_t);
39 static int seghole_sync(struct seg *, caddr_t, size_t, int, uint_t);
40 static size_t seghole_incore(struct seg *, caddr_t, size_t, char *);
41 static int seghole_lockop(struct seg *, caddr_t, size_t, int, int, ulong_t *,
42     size_t);
43 static int seghole_getprot(struct seg *, caddr_t, size_t, uint_t *);
44 static u_offset_t seghole_getoffset(struct seg *, caddr_t);
45 static int seghole_gettype(struct seg *, caddr_t);
46 static int seghole_getvp(struct seg *, caddr_t, struct vnode **);
47 static int seghole_advise(struct seg *, caddr_t, size_t, uint_t);
48 static void seghole_dump(struct seg *);
49 static int seghole_pagelock(struct seg *, caddr_t, size_t, struct page ***,
50     enum lock_type, enum seg_rw);
51 static int seghole_setpagesize(struct seg *, caddr_t, size_t, uint_t);
52 static int seghole_capable(struct seg *, segcapability_t);
53 
54 static struct seg_ops seghole_ops = {
55 	seghole_dup,
56 	seghole_unmap,
57 	seghole_free,
58 	seghole_fault,
59 	seghole_faulta,
60 	seghole_setprot,
61 	seghole_checkprot,
62 	NULL,			/* kluster: disabled */
63 	NULL,			/* swapout: disabled */
64 	seghole_sync,
65 	seghole_incore,
66 	seghole_lockop,
67 	seghole_getprot,
68 	seghole_getoffset,
69 	seghole_gettype,
70 	seghole_getvp,
71 	seghole_advise,
72 	seghole_dump,
73 	seghole_pagelock,
74 	seghole_setpagesize,
75 	NULL,			/* getmemid: disabled */
76 	NULL,			/* getpolicy: disabled */
77 	seghole_capable,
78 	seg_inherit_notsup
79 };
80 
81 /*
82  * Create a hole in the AS.
83  */
84 int
85 seghole_create(struct seg **segpp, void *argsp)
86 {
87 	struct seg *seg = *segpp;
88 	seghole_crargs_t *crargs = argsp;
89 	seghole_data_t *data;
90 
91 	data = kmem_alloc(sizeof (seghole_data_t), KM_SLEEP);
92 	data->shd_name = crargs->name;
93 
94 	seg->s_ops = &seghole_ops;
95 	seg->s_data = data;
96 	seg->s_flags = S_HOLE;
97 
98 	return (0);
99 }
100 
101 static int
102 seghole_dup(struct seg *seg, struct seg *newseg)
103 {
104 	seghole_data_t *shd = (seghole_data_t *)seg->s_data;
105 	seghole_data_t *newshd;
106 
107 	ASSERT(seg->s_as && AS_WRITE_HELD(seg->s_as));
108 
109 	newshd = kmem_zalloc(sizeof (seghole_data_t), KM_SLEEP);
110 	newshd->shd_name = shd->shd_name;
111 
112 	newseg->s_ops = seg->s_ops;
113 	newseg->s_data = newshd;
114 	newseg->s_flags = S_HOLE;
115 
116 	return (0);
117 }
118 
119 static int
120 seghole_unmap(struct seg *seg, caddr_t addr, size_t len)
121 {
122 	ASSERT(seg->s_as && AS_WRITE_HELD(seg->s_as));
123 
124 	/* Entire segment is being unmapped */
125 	if (addr == seg->s_base && len == seg->s_size) {
126 		seg_free(seg);
127 		return (0);
128 	}
129 
130 	/* Shrinking from low address side */
131 	if (addr == seg->s_base) {
132 		seg->s_base += len;
133 		seg->s_size -= len;
134 		return (0);
135 	}
136 
137 	/* Shrinking from high address side */
138 	if ((addr + len) == (seg->s_base + seg->s_size)) {
139 		seg->s_size -= len;
140 		return (0);
141 	}
142 
143 	/* Do not tolerate splitting the segment */
144 	return (EINVAL);
145 }
146 
147 static void
148 seghole_free(struct seg *seg)
149 {
150 	seghole_data_t *data = (seghole_data_t *)seg->s_data;
151 
152 	ASSERT(data != NULL);
153 
154 	kmem_free(data, sizeof (*data));
155 	seg->s_data = NULL;
156 }
157 
158 /* ARGSUSED */
159 static faultcode_t
160 seghole_fault(struct hat *hat, struct seg *seg, caddr_t addr, size_t len,
161     enum fault_type type, enum seg_rw tw)
162 {
163 	ASSERT(seg->s_as && AS_LOCK_HELD(seg->s_as));
164 
165 	return (FC_NOMAP);
166 }
167 
168 /* ARGSUSED */
169 static faultcode_t
170 seghole_faulta(struct seg *seg, caddr_t addr)
171 {
172 	return (FC_NOMAP);
173 }
174 
175 /* ARGSUSED */
176 static int
177 seghole_setprot(struct seg *seg, caddr_t addr, size_t len, uint_t prot)
178 {
179 	ASSERT(seg->s_as && AS_LOCK_HELD(seg->s_as));
180 
181 	return (ENOMEM);
182 }
183 
184 /* ARGSUSED */
185 static int
186 seghole_checkprot(struct seg *seg, caddr_t addr, size_t len, uint_t prot)
187 {
188 	ASSERT(seg->s_as && AS_LOCK_HELD(seg->s_as));
189 
190 	return (ENOMEM);
191 }
192 
193 /* ARGSUSED */
194 static int
195 seghole_sync(struct seg *seg, caddr_t addr, size_t len, int attr, uint_t flags)
196 {
197 	/* Always succeed since there are no backing store to sync */
198 	return (0);
199 }
200 
201 /* ARGSUSED */
202 static size_t
203 seghole_incore(struct seg *seg, caddr_t addr, size_t len, char *vec)
204 {
205 	ASSERT(seg->s_as && AS_LOCK_HELD(seg->s_as));
206 
207 	return (0);
208 }
209 
210 /* ARGSUSED */
211 static int
212 seghole_lockop(struct seg *seg, caddr_t addr, size_t len, int attr, int op,
213     ulong_t *lockmap, size_t pos)
214 {
215 	/*
216 	 * Emit an error consistent with there being no segment in this hole in
217 	 * the AS.  The MC_LOCKAS and MC_UNLOCKAS commands will explicitly skip
218 	 * hole segments, allowing such operations to proceed as expected.
219 	 */
220 	return (ENOMEM);
221 }
222 
223 static int
224 seghole_getprot(struct seg *seg, caddr_t addr, size_t len, uint_t *protv)
225 {
226 	size_t pgno;
227 
228 	ASSERT(seg->s_as && AS_LOCK_HELD(seg->s_as));
229 
230 	/*
231 	 * Few SEGOP_GETPROT callers actually check for an error, so it's
232 	 * necessary to report zeroed protection for the length of the request.
233 	 */
234 	pgno = seg_page(seg, addr + len) - seg_page(seg, addr) + 1;
235 	while (pgno > 0) {
236 		protv[--pgno] = 0;
237 	}
238 
239 	return (ENOMEM);
240 }
241 
242 /* ARGSUSED */
243 static u_offset_t
244 seghole_getoffset(struct seg *seg, caddr_t addr)
245 {
246 	/*
247 	 * To avoid leaking information about the layout of the kernel address
248 	 * space, always report '0' as the offset.
249 	 */
250 	return (0);
251 }
252 
253 /* ARGSUSED */
254 static int
255 seghole_gettype(struct seg *seg, caddr_t addr)
256 {
257 	return (MAP_PRIVATE);
258 }
259 
260 /* ARGSUSED */
261 static int
262 seghole_getvp(struct seg *seg, caddr_t addr, struct vnode **vpp)
263 {
264 	ASSERT(seg->s_as && AS_LOCK_HELD(seg->s_as));
265 
266 	return (ENOMEM);
267 }
268 
269 /* ARGSUSED */
270 static int
271 seghole_advise(struct seg *seg, caddr_t addr, size_t len, uint_t behav)
272 {
273 	return (ENOMEM);
274 }
275 
276 /* ARGSUSED */
277 static void
278 seghole_dump(struct seg *seg)
279 {
280 	/* There's nothing to dump from a hole in the AS */
281 }
282 
283 /* ARGSUSED */
284 static int
285 seghole_pagelock(struct seg *seg, caddr_t addr, size_t len, struct page ***ppp,
286     enum lock_type type, enum seg_rw rw)
287 {
288 	return (EFAULT);
289 }
290 
291 /* ARGSUSED */
292 static int
293 seghole_setpagesize(struct seg *seg, caddr_t addr, size_t len, uint_t szc)
294 {
295 	return (ENOMEM);
296 }
297 
298 /* ARGSUSED */
299 static int
300 seghole_capable(struct seg *seg, segcapability_t capability)
301 {
302 	/* no special capablities */
303 	return (0);
304 }
305