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
seghole_create(struct seg ** segpp,void * argsp)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
seghole_dup(struct seg * seg,struct seg * newseg)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
seghole_unmap(struct seg * seg,caddr_t addr,size_t len)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
seghole_free(struct seg * seg)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
seghole_fault(struct hat * hat,struct seg * seg,caddr_t addr,size_t len,enum fault_type type,enum seg_rw tw)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
seghole_faulta(struct seg * seg,caddr_t addr)170 seghole_faulta(struct seg *seg, caddr_t addr)
171 {
172 return (FC_NOMAP);
173 }
174
175 /* ARGSUSED */
176 static int
seghole_setprot(struct seg * seg,caddr_t addr,size_t len,uint_t prot)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
seghole_checkprot(struct seg * seg,caddr_t addr,size_t len,uint_t prot)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
seghole_sync(struct seg * seg,caddr_t addr,size_t len,int attr,uint_t flags)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
seghole_incore(struct seg * seg,caddr_t addr,size_t len,char * vec)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
seghole_lockop(struct seg * seg,caddr_t addr,size_t len,int attr,int op,ulong_t * lockmap,size_t pos)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
seghole_getprot(struct seg * seg,caddr_t addr,size_t len,uint_t * protv)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
seghole_getoffset(struct seg * seg,caddr_t addr)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
seghole_gettype(struct seg * seg,caddr_t addr)255 seghole_gettype(struct seg *seg, caddr_t addr)
256 {
257 return (MAP_PRIVATE);
258 }
259
260 /* ARGSUSED */
261 static int
seghole_getvp(struct seg * seg,caddr_t addr,struct vnode ** vpp)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
seghole_advise(struct seg * seg,caddr_t addr,size_t len,uint_t behav)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
seghole_dump(struct seg * seg)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
seghole_pagelock(struct seg * seg,caddr_t addr,size_t len,struct page *** ppp,enum lock_type type,enum seg_rw rw)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
seghole_setpagesize(struct seg * seg,caddr_t addr,size_t len,uint_t szc)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
seghole_capable(struct seg * seg,segcapability_t capability)300 seghole_capable(struct seg *seg, segcapability_t capability)
301 {
302 /* no special capablities */
303 return (0);
304 }
305