/* * Copyright (c) 2018 Cisco and/or its affiliates. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <unistd.h> #include <sys/types.h> #include <sys/mount.h> #include <sys/mman.h> #include <sys/fcntl.h> #include <sys/stat.h> #include <unistd.h> #include <vppinfra/linux/sysfs.h> #include <vlib/vlib.h> #include <vlib/physmem.h> #include <vlib/unix/unix.h> #include <vlib/pci/pci.h> #include <vlib/linux/vfio.h> #if defined(__x86_64__) && !defined(CLIB_SANITIZE_ADDR) /* we keep physmem in low 38 bits of VA address space as some IOMMU implamentation cannot map above that range */ #define VLIB_PHYSMEM_DEFAULT_BASE_ADDDR (1ULL << 36) #else /* let kernel decide */ #define VLIB_PHYSMEM_DEFAULT_BASE_ADDDR 0 #endif clib_error_t * vlib_physmem_shared_map_create (vlib_main_t * vm, char *name, uword size, u32 log2_page_sz, u32 numa_node, u32 * map_index) { clib_pmalloc_main_t *pm = vm->physmem_main.pmalloc_main; vlib_physmem_main_t *vpm = &vm->physmem_main; vlib_physmem_map_t *map; clib_pmalloc_arena_t *a; clib_error_t *error = 0; void *va; uword i; va = clib_pmalloc_create_shared_arena (pm, name, size, log2_page_sz, numa_node); if (va == 0) return clib_error_return (0, "%U", format_clib_error, clib_pmalloc_last_error (pm)); a = clib_pmalloc_get_arena (pm, va); pool_get (vpm->maps, map); *map_index = map->index = map - vpm->maps; map->base = va; map->fd = a->fd; map->n_pages = a->n_pages * a->subpages_per_page; map->log2_page_size = a->log2_subpage_sz; map->numa_node = a->numa_node; for (i = 0; i < a->n_pages; i++) { uword pa = clib_pmalloc_get_pa (pm, (u8 *) va + (i << a->log2_subpage_sz)); /* maybe iova */ if (pa == 0) pa = pointer_to_uword (va); vec_add1 (map->page_table, pa); } return error; } vlib_physmem_map_t * vlib_physmem_get_map (vlib_main_t * vm, u32 index) { vlib_physmem_main_t *vpm = &vm->physmem_main; return pool_elt_at_index (vpm->maps, index); } clib_error_t * vlib_physmem_init (vlib_main_t * vm) { vlib_physmem_main_t *vpm = &vm->physmem_main; clib_error_t *error = 0; u64 *pt = 0; void *p; /* check if pagemap is accessible */ pt = clib_mem_vm_get_paddr (&pt, min_log2 (sysconf (_SC_PAGESIZE)), 1); if (pt && pt[0]) vpm->flags |= VLIB_PHYSMEM_MAIN_F_HAVE_PAGEMAP; vec_free (pt); if ((error = linux_vfio_init (vm))) return error; p = clib_mem_alloc_aligned (sizeof (clib_pmalloc_main_t), CLIB_CACHE_LINE_BYTES); memset (p, 0, sizeof (clib_pmalloc_main_t)); vpm->pmalloc_main = (clib_pmalloc_main_t *) p; if (vpm->base_addr == 0) vpm->base_addr = VLIB_PHYSMEM_DEFAULT_BASE_ADDDR; clib_pmalloc_init (vpm->pmalloc_main, vpm->base_addr, vpm->max_size); /* update base_addr and max_size per actual allocation */ vpm->base_addr = (uword) vpm->pmalloc_main->base; vpm->max_size = (uword) vpm->pmalloc_main->max_pages << vpm->pmalloc_main->def_log2_page_sz; return error; } static clib_error_t * show_physmem (vlib_main_t * vm, unformat_input_t * input, vlib_cli_command_t * cmd) { vlib_physmem_main_t *vpm = &vm->physmem_main; unformat_input_t _line_input, *line_input = &_line_input; u32 verbose = 0, map = 0; if (unformat_user (input, unformat_line_input, line_input)) { while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT) { if (unformat (line_input, "verbose")) verbose = 1; else if (unformat (line_input, "v")) verbose = 1; else if (unformat (line_input, "detail")) verbose = 2; else if (unformat (line_input, "d")) verbose = 2; else if (unformat (line_input, "map")) map = 1; else break; } unformat_free (line_input); } if (map) vlib_cli_output (vm, " %U", format_pmalloc_map, vpm->pmalloc_main); else vlib_cli_output (vm, " %U", format_pmalloc, vpm->pmalloc_main, verbose); return 0; } /* *INDENT-OFF* */ VLIB_CLI_COMMAND (show_physmem_command, static) = { .path = "show physmem", .short_help = "show physmem [verbose | detail | map]", .function = show_physmem, }; /* *INDENT-ON* */ static clib_error_t * vlib_physmem_config (vlib_main_t * vm, unformat_input_t * input) { vlib_physmem_main_t *vpm = &vm->physmem_main; while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { if (unformat (input, "base-addr 0x%lx", &vpm->base_addr)) ; else if (unformat (input, "max-size %U", unformat_memory_size, &vpm->max_size)) ; else return unformat_parse_error (input); } unformat_free (input); return 0; } VLIB_EARLY_CONFIG_FUNCTION (vlib_physmem_config, "physmem"); /* * fd.io coding-style-patch-verification: ON * * Local Variables: * eval: (c-set-style "gnu") * End: */