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