1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright (c) 2020 Dmitry Kozlyuk 3 */ 4 5 #include <rte_errno.h> 6 7 #include "eal_internal_cfg.h" 8 #include "eal_memalloc.h" 9 #include "eal_memcfg.h" 10 #include "eal_private.h" 11 #include "eal_windows.h" 12 13 int 14 eal_memalloc_get_seg_fd(int list_idx, int seg_idx) 15 { 16 /* Hugepages have no associated files in Windows. */ 17 RTE_SET_USED(list_idx); 18 RTE_SET_USED(seg_idx); 19 EAL_LOG_NOT_IMPLEMENTED(); 20 return -1; 21 } 22 23 int 24 eal_memalloc_get_seg_fd_offset(int list_idx, int seg_idx, size_t *offset) 25 { 26 /* Hugepages have no associated files in Windows. */ 27 RTE_SET_USED(list_idx); 28 RTE_SET_USED(seg_idx); 29 RTE_SET_USED(offset); 30 EAL_LOG_NOT_IMPLEMENTED(); 31 return -1; 32 } 33 34 static int 35 alloc_seg(struct rte_memseg *ms, void *requested_addr, int socket_id, 36 struct hugepage_info *hi) 37 { 38 HANDLE current_process; 39 unsigned int numa_node; 40 size_t alloc_sz; 41 void *addr; 42 rte_iova_t iova = RTE_BAD_IOVA; 43 PSAPI_WORKING_SET_EX_INFORMATION info; 44 PSAPI_WORKING_SET_EX_BLOCK *page; 45 46 if (ms->len > 0) { 47 /* If a segment is already allocated as needed, return it. */ 48 if ((ms->addr == requested_addr) && 49 (ms->socket_id == socket_id) && 50 (ms->hugepage_sz == hi->hugepage_sz)) { 51 return 0; 52 } 53 54 /* Bugcheck, should not happen. */ 55 RTE_LOG(DEBUG, EAL, "Attempted to reallocate segment %p " 56 "(size %zu) on socket %d", ms->addr, 57 ms->len, ms->socket_id); 58 return -1; 59 } 60 61 current_process = GetCurrentProcess(); 62 numa_node = eal_socket_numa_node(socket_id); 63 alloc_sz = hi->hugepage_sz; 64 65 if (requested_addr == NULL) { 66 /* Request a new chunk of memory from OS. */ 67 addr = eal_mem_alloc_socket(alloc_sz, socket_id); 68 if (addr == NULL) { 69 RTE_LOG(DEBUG, EAL, "Cannot allocate %zu bytes " 70 "on socket %d\n", alloc_sz, socket_id); 71 return -1; 72 } 73 } else { 74 /* Requested address is already reserved, commit memory. */ 75 addr = eal_mem_commit(requested_addr, alloc_sz, socket_id); 76 77 /* During commitment, memory is temporary freed and might 78 * be allocated by different non-EAL thread. This is a fatal 79 * error, because it breaks MSL assumptions. 80 */ 81 if ((addr != NULL) && (addr != requested_addr)) { 82 RTE_LOG(CRIT, EAL, "Address %p occupied by an alien " 83 " allocation - MSL is not VA-contiguous!\n", 84 requested_addr); 85 return -1; 86 } 87 88 if (addr == NULL) { 89 RTE_LOG(DEBUG, EAL, "Cannot commit reserved memory %p " 90 "(size %zu) on socket %d\n", 91 requested_addr, alloc_sz, socket_id); 92 return -1; 93 } 94 } 95 96 /* Force OS to allocate a physical page and select a NUMA node. 97 * Hugepages are not pageable in Windows, so there's no race 98 * for physical address. 99 */ 100 *(volatile int *)addr = *(volatile int *)addr; 101 102 /* Only try to obtain IOVA if it's available, so that applications 103 * that do not need IOVA can use this allocator. 104 */ 105 if (rte_eal_using_phys_addrs()) { 106 iova = rte_mem_virt2iova(addr); 107 if (iova == RTE_BAD_IOVA) { 108 RTE_LOG(DEBUG, EAL, 109 "Cannot get IOVA of allocated segment\n"); 110 goto error; 111 } 112 } 113 114 /* Only "Ex" function can handle hugepages. */ 115 info.VirtualAddress = addr; 116 if (!QueryWorkingSetEx(current_process, &info, sizeof(info))) { 117 RTE_LOG_WIN32_ERR("QueryWorkingSetEx(%p)", addr); 118 goto error; 119 } 120 121 page = &info.VirtualAttributes; 122 if (!page->Valid || !page->LargePage) { 123 RTE_LOG(DEBUG, EAL, "Got regular page instead of a hugepage\n"); 124 goto error; 125 } 126 if (page->Node != numa_node) { 127 RTE_LOG(DEBUG, EAL, 128 "NUMA node hint %u (socket %d) not respected, got %u\n", 129 numa_node, socket_id, page->Node); 130 goto error; 131 } 132 133 ms->addr = addr; 134 ms->hugepage_sz = hi->hugepage_sz; 135 ms->len = alloc_sz; 136 ms->nchannel = rte_memory_get_nchannel(); 137 ms->nrank = rte_memory_get_nrank(); 138 ms->iova = iova; 139 ms->socket_id = socket_id; 140 141 return 0; 142 143 error: 144 /* Only jump here when `addr` and `alloc_sz` are valid. */ 145 if (eal_mem_decommit(addr, alloc_sz) && (rte_errno == EADDRNOTAVAIL)) { 146 /* During decommitment, memory is temporarily returned 147 * to the system and the address may become unavailable. 148 */ 149 RTE_LOG(CRIT, EAL, "Address %p occupied by an alien " 150 " allocation - MSL is not VA-contiguous!\n", addr); 151 } 152 return -1; 153 } 154 155 static int 156 free_seg(struct rte_memseg *ms) 157 { 158 if (eal_mem_decommit(ms->addr, ms->len)) { 159 if (rte_errno == EADDRNOTAVAIL) { 160 /* See alloc_seg() for explanation. */ 161 RTE_LOG(CRIT, EAL, "Address %p occupied by an alien " 162 " allocation - MSL is not VA-contiguous!\n", 163 ms->addr); 164 } 165 return -1; 166 } 167 168 /* Must clear the segment, because alloc_seg() inspects it. */ 169 memset(ms, 0, sizeof(*ms)); 170 return 0; 171 } 172 173 struct alloc_walk_param { 174 struct hugepage_info *hi; 175 struct rte_memseg **ms; 176 size_t page_sz; 177 unsigned int segs_allocated; 178 unsigned int n_segs; 179 int socket; 180 bool exact; 181 }; 182 183 static int 184 alloc_seg_walk(const struct rte_memseg_list *msl, void *arg) 185 { 186 struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config; 187 struct alloc_walk_param *wa = arg; 188 struct rte_memseg_list *cur_msl; 189 size_t page_sz; 190 int cur_idx, start_idx, j; 191 unsigned int msl_idx, need, i; 192 193 if (msl->page_sz != wa->page_sz) 194 return 0; 195 if (msl->socket_id != wa->socket) 196 return 0; 197 198 page_sz = (size_t)msl->page_sz; 199 200 msl_idx = msl - mcfg->memsegs; 201 cur_msl = &mcfg->memsegs[msl_idx]; 202 203 need = wa->n_segs; 204 205 /* try finding space in memseg list */ 206 if (wa->exact) { 207 /* if we require exact number of pages in a list, find them */ 208 cur_idx = rte_fbarray_find_next_n_free( 209 &cur_msl->memseg_arr, 0, need); 210 if (cur_idx < 0) 211 return 0; 212 start_idx = cur_idx; 213 } else { 214 int cur_len; 215 216 /* we don't require exact number of pages, so we're going to go 217 * for best-effort allocation. that means finding the biggest 218 * unused block, and going with that. 219 */ 220 cur_idx = rte_fbarray_find_biggest_free( 221 &cur_msl->memseg_arr, 0); 222 if (cur_idx < 0) 223 return 0; 224 start_idx = cur_idx; 225 /* adjust the size to possibly be smaller than original 226 * request, but do not allow it to be bigger. 227 */ 228 cur_len = rte_fbarray_find_contig_free( 229 &cur_msl->memseg_arr, cur_idx); 230 need = RTE_MIN(need, (unsigned int)cur_len); 231 } 232 233 for (i = 0; i < need; i++, cur_idx++) { 234 struct rte_memseg *cur; 235 void *map_addr; 236 237 cur = rte_fbarray_get(&cur_msl->memseg_arr, cur_idx); 238 map_addr = RTE_PTR_ADD(cur_msl->base_va, cur_idx * page_sz); 239 240 if (alloc_seg(cur, map_addr, wa->socket, wa->hi)) { 241 RTE_LOG(DEBUG, EAL, "attempted to allocate %i segments, " 242 "but only %i were allocated\n", need, i); 243 244 /* if exact number wasn't requested, stop */ 245 if (!wa->exact) 246 goto out; 247 248 /* clean up */ 249 for (j = start_idx; j < cur_idx; j++) { 250 struct rte_memseg *tmp; 251 struct rte_fbarray *arr = &cur_msl->memseg_arr; 252 253 tmp = rte_fbarray_get(arr, j); 254 rte_fbarray_set_free(arr, j); 255 256 if (free_seg(tmp)) 257 RTE_LOG(DEBUG, EAL, "Cannot free page\n"); 258 } 259 /* clear the list */ 260 if (wa->ms) 261 memset(wa->ms, 0, sizeof(*wa->ms) * wa->n_segs); 262 263 return -1; 264 } 265 if (wa->ms) 266 wa->ms[i] = cur; 267 268 rte_fbarray_set_used(&cur_msl->memseg_arr, cur_idx); 269 } 270 271 out: 272 wa->segs_allocated = i; 273 if (i > 0) 274 cur_msl->version++; 275 276 /* if we didn't allocate any segments, move on to the next list */ 277 return i > 0; 278 } 279 280 struct free_walk_param { 281 struct hugepage_info *hi; 282 struct rte_memseg *ms; 283 }; 284 static int 285 free_seg_walk(const struct rte_memseg_list *msl, void *arg) 286 { 287 struct rte_mem_config *mcfg = rte_eal_get_configuration()->mem_config; 288 struct rte_memseg_list *found_msl; 289 struct free_walk_param *wa = arg; 290 uintptr_t start_addr, end_addr; 291 int msl_idx, seg_idx, ret; 292 293 start_addr = (uintptr_t) msl->base_va; 294 end_addr = start_addr + msl->len; 295 296 if ((uintptr_t)wa->ms->addr < start_addr || 297 (uintptr_t)wa->ms->addr >= end_addr) 298 return 0; 299 300 msl_idx = msl - mcfg->memsegs; 301 seg_idx = RTE_PTR_DIFF(wa->ms->addr, start_addr) / msl->page_sz; 302 303 /* msl is const */ 304 found_msl = &mcfg->memsegs[msl_idx]; 305 found_msl->version++; 306 307 rte_fbarray_set_free(&found_msl->memseg_arr, seg_idx); 308 309 ret = free_seg(wa->ms); 310 311 return (ret < 0) ? (-1) : 1; 312 } 313 314 int 315 eal_memalloc_alloc_seg_bulk(struct rte_memseg **ms, int n_segs, 316 size_t page_sz, int socket, bool exact) 317 { 318 unsigned int i; 319 int ret = -1; 320 struct alloc_walk_param wa; 321 struct hugepage_info *hi = NULL; 322 struct internal_config *internal_conf = 323 eal_get_internal_configuration(); 324 325 if (internal_conf->legacy_mem) { 326 RTE_LOG(ERR, EAL, "dynamic allocation not supported in legacy mode\n"); 327 return -ENOTSUP; 328 } 329 330 for (i = 0; i < internal_conf->num_hugepage_sizes; i++) { 331 struct hugepage_info *hpi = &internal_conf->hugepage_info[i]; 332 if (page_sz == hpi->hugepage_sz) { 333 hi = hpi; 334 break; 335 } 336 } 337 if (!hi) { 338 RTE_LOG(ERR, EAL, "cannot find relevant hugepage_info entry\n"); 339 return -1; 340 } 341 342 memset(&wa, 0, sizeof(wa)); 343 wa.exact = exact; 344 wa.hi = hi; 345 wa.ms = ms; 346 wa.n_segs = n_segs; 347 wa.page_sz = page_sz; 348 wa.socket = socket; 349 wa.segs_allocated = 0; 350 351 /* memalloc is locked, so it's safe to use thread-unsafe version */ 352 ret = rte_memseg_list_walk_thread_unsafe(alloc_seg_walk, &wa); 353 if (ret == 0) { 354 RTE_LOG(ERR, EAL, "cannot find suitable memseg_list\n"); 355 ret = -1; 356 } else if (ret > 0) { 357 ret = (int)wa.segs_allocated; 358 } 359 360 return ret; 361 } 362 363 struct rte_memseg * 364 eal_memalloc_alloc_seg(size_t page_sz, int socket) 365 { 366 struct rte_memseg *ms = NULL; 367 eal_memalloc_alloc_seg_bulk(&ms, 1, page_sz, socket, true); 368 return ms; 369 } 370 371 int 372 eal_memalloc_free_seg_bulk(struct rte_memseg **ms, int n_segs) 373 { 374 int seg, ret = 0; 375 struct internal_config *internal_conf = 376 eal_get_internal_configuration(); 377 378 /* dynamic free not supported in legacy mode */ 379 if (internal_conf->legacy_mem) 380 return -1; 381 382 for (seg = 0; seg < n_segs; seg++) { 383 struct rte_memseg *cur = ms[seg]; 384 struct hugepage_info *hi = NULL; 385 struct free_walk_param wa; 386 size_t i; 387 int walk_res; 388 389 /* if this page is marked as unfreeable, fail */ 390 if (cur->flags & RTE_MEMSEG_FLAG_DO_NOT_FREE) { 391 RTE_LOG(DEBUG, EAL, "Page is not allowed to be freed\n"); 392 ret = -1; 393 continue; 394 } 395 396 memset(&wa, 0, sizeof(wa)); 397 398 for (i = 0; i < RTE_DIM(internal_conf->hugepage_info); i++) { 399 hi = &internal_conf->hugepage_info[i]; 400 if (cur->hugepage_sz == hi->hugepage_sz) 401 break; 402 } 403 if (i == RTE_DIM(internal_conf->hugepage_info)) { 404 RTE_LOG(ERR, EAL, "Can't find relevant hugepage_info entry\n"); 405 ret = -1; 406 continue; 407 } 408 409 wa.ms = cur; 410 wa.hi = hi; 411 412 /* memalloc is locked, so it's safe to use thread-unsafe version 413 */ 414 walk_res = rte_memseg_list_walk_thread_unsafe(free_seg_walk, 415 &wa); 416 if (walk_res == 1) 417 continue; 418 if (walk_res == 0) 419 RTE_LOG(ERR, EAL, "Couldn't find memseg list\n"); 420 ret = -1; 421 } 422 return ret; 423 } 424 425 int 426 eal_memalloc_free_seg(struct rte_memseg *ms) 427 { 428 return eal_memalloc_free_seg_bulk(&ms, 1); 429 } 430 431 int 432 eal_memalloc_sync_with_primary(void) 433 { 434 /* No multi-process support. */ 435 EAL_LOG_NOT_IMPLEMENTED(); 436 return -1; 437 } 438 439 int 440 eal_memalloc_cleanup(void) 441 { 442 /* not implemented */ 443 return 0; 444 } 445 446 int 447 eal_memalloc_init(void) 448 { 449 /* No action required. */ 450 return 0; 451 } 452