11739a20eSAndy Ritger /*******************************************************************************
2*91676d66SBernhard Stoeckner     Copyright (c) 2018-2023 NVIDIA Corporation
31739a20eSAndy Ritger 
41739a20eSAndy Ritger     Permission is hereby granted, free of charge, to any person obtaining a copy
51739a20eSAndy Ritger     of this software and associated documentation files (the "Software"), to
61739a20eSAndy Ritger     deal in the Software without restriction, including without limitation the
71739a20eSAndy Ritger     rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
81739a20eSAndy Ritger     sell copies of the Software, and to permit persons to whom the Software is
91739a20eSAndy Ritger     furnished to do so, subject to the following conditions:
101739a20eSAndy Ritger 
111739a20eSAndy Ritger         The above copyright notice and this permission notice shall be
121739a20eSAndy Ritger         included in all copies or substantial portions of the Software.
131739a20eSAndy Ritger 
141739a20eSAndy Ritger     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
151739a20eSAndy Ritger     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
161739a20eSAndy Ritger     FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
171739a20eSAndy Ritger     THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
181739a20eSAndy Ritger     LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
191739a20eSAndy Ritger     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
201739a20eSAndy Ritger     DEALINGS IN THE SOFTWARE.
211739a20eSAndy Ritger 
221739a20eSAndy Ritger *******************************************************************************/
231739a20eSAndy Ritger 
241739a20eSAndy Ritger #include "uvm_common.h"
251739a20eSAndy Ritger #include "uvm_linux.h"
261739a20eSAndy Ritger #include "uvm_gpu.h"
271739a20eSAndy Ritger #include "uvm_lock.h"
281739a20eSAndy Ritger #include "uvm_va_space.h"
291739a20eSAndy Ritger #include "uvm_tracker.h"
301739a20eSAndy Ritger #include "uvm_api.h"
311739a20eSAndy Ritger #include "uvm_push.h"
321739a20eSAndy Ritger #include "uvm_hal.h"
331739a20eSAndy Ritger #include "uvm_migrate_pageable.h"
341739a20eSAndy Ritger #include "uvm_populate_pageable.h"
351739a20eSAndy Ritger 
361739a20eSAndy Ritger #ifdef UVM_MIGRATE_VMA_SUPPORTED
371739a20eSAndy Ritger 
381739a20eSAndy Ritger static struct kmem_cache *g_uvm_migrate_vma_state_cache __read_mostly;
391739a20eSAndy Ritger 
401739a20eSAndy Ritger static const gfp_t g_migrate_vma_gfp_flags = NV_UVM_GFP_FLAGS | GFP_HIGHUSER_MOVABLE | __GFP_THISNODE;
411739a20eSAndy Ritger 
421739a20eSAndy Ritger // Compute the address needed for copying_gpu to access the given page,
431739a20eSAndy Ritger // resident on resident_id.
migrate_vma_page_copy_address(struct page * page,unsigned long page_index,uvm_processor_id_t resident_id,uvm_gpu_t * copying_gpu,migrate_vma_state_t * state,uvm_gpu_address_t * gpu_addr)441739a20eSAndy Ritger static NV_STATUS migrate_vma_page_copy_address(struct page *page,
451739a20eSAndy Ritger                                                unsigned long page_index,
461739a20eSAndy Ritger                                                uvm_processor_id_t resident_id,
471739a20eSAndy Ritger                                                uvm_gpu_t *copying_gpu,
481739a20eSAndy Ritger                                                migrate_vma_state_t *state,
491739a20eSAndy Ritger                                                uvm_gpu_address_t *gpu_addr)
501739a20eSAndy Ritger {
511739a20eSAndy Ritger     uvm_va_space_t *va_space = state->uvm_migrate_args->va_space;
521739a20eSAndy Ritger     uvm_gpu_t *owning_gpu = UVM_ID_IS_CPU(resident_id)? NULL: uvm_va_space_get_gpu(va_space, resident_id);
531739a20eSAndy Ritger     const bool can_copy_from = uvm_processor_mask_test(&va_space->can_copy_from[uvm_id_value(copying_gpu->id)],
541739a20eSAndy Ritger                                                        resident_id);
551739a20eSAndy Ritger     const bool direct_peer = owning_gpu &&
561739a20eSAndy Ritger                              (owning_gpu != copying_gpu) &&
571739a20eSAndy Ritger                              can_copy_from &&
581739a20eSAndy Ritger                              !uvm_gpu_peer_caps(owning_gpu, copying_gpu)->is_indirect_peer;
591739a20eSAndy Ritger 
601739a20eSAndy Ritger     UVM_ASSERT(page_index < state->num_pages);
611739a20eSAndy Ritger 
621739a20eSAndy Ritger     memset(gpu_addr, 0, sizeof(*gpu_addr));
631739a20eSAndy Ritger 
641739a20eSAndy Ritger     if (owning_gpu == copying_gpu) {
651739a20eSAndy Ritger         // Local vidmem address
66eb5c7665SAndy Ritger         *gpu_addr = uvm_gpu_address_copy(owning_gpu, uvm_gpu_page_to_phys_address(owning_gpu, page));
671739a20eSAndy Ritger     }
681739a20eSAndy Ritger     else if (direct_peer) {
691739a20eSAndy Ritger         // Direct GPU peer
701739a20eSAndy Ritger         uvm_gpu_identity_mapping_t *gpu_peer_mappings = uvm_gpu_get_peer_mapping(copying_gpu, owning_gpu->id);
711739a20eSAndy Ritger         uvm_gpu_phys_address_t phys_addr = uvm_gpu_page_to_phys_address(owning_gpu, page);
721739a20eSAndy Ritger 
731739a20eSAndy Ritger         *gpu_addr = uvm_gpu_address_virtual(gpu_peer_mappings->base + phys_addr.address);
741739a20eSAndy Ritger     }
751739a20eSAndy Ritger     else {
761739a20eSAndy Ritger         // Sysmem/Indirect Peer
77*91676d66SBernhard Stoeckner         NV_STATUS status = uvm_parent_gpu_map_cpu_page(copying_gpu->parent, page, &state->dma.addrs[page_index]);
781739a20eSAndy Ritger 
791739a20eSAndy Ritger         if (status != NV_OK)
801739a20eSAndy Ritger             return status;
811739a20eSAndy Ritger 
821739a20eSAndy Ritger         state->dma.addrs_gpus[page_index] = copying_gpu;
831739a20eSAndy Ritger 
841739a20eSAndy Ritger         if (state->dma.num_pages++ == 0)
851739a20eSAndy Ritger             bitmap_zero(state->dma.page_mask, state->num_pages);
861739a20eSAndy Ritger 
871739a20eSAndy Ritger         UVM_ASSERT(!test_bit(page_index, state->dma.page_mask));
881739a20eSAndy Ritger 
891739a20eSAndy Ritger         __set_bit(page_index, state->dma.page_mask);
901739a20eSAndy Ritger 
91eb5c7665SAndy Ritger         *gpu_addr = uvm_gpu_address_copy(copying_gpu,
92eb5c7665SAndy Ritger                                          uvm_gpu_phys_address(UVM_APERTURE_SYS, state->dma.addrs[page_index]));
931739a20eSAndy Ritger     }
941739a20eSAndy Ritger 
951739a20eSAndy Ritger     return NV_OK;
961739a20eSAndy Ritger }
971739a20eSAndy Ritger 
981739a20eSAndy Ritger // Create a new push to zero pages on dst_id
migrate_vma_zero_begin_push(uvm_va_space_t * va_space,uvm_processor_id_t dst_id,uvm_gpu_t * gpu,unsigned long start,unsigned long outer,uvm_push_t * push)991739a20eSAndy Ritger static NV_STATUS migrate_vma_zero_begin_push(uvm_va_space_t *va_space,
1001739a20eSAndy Ritger                                              uvm_processor_id_t dst_id,
1011739a20eSAndy Ritger                                              uvm_gpu_t *gpu,
1021739a20eSAndy Ritger                                              unsigned long start,
1031739a20eSAndy Ritger                                              unsigned long outer,
1041739a20eSAndy Ritger                                              uvm_push_t *push)
1051739a20eSAndy Ritger {
1061739a20eSAndy Ritger     uvm_channel_type_t channel_type;
1071739a20eSAndy Ritger 
1081739a20eSAndy Ritger     if (UVM_ID_IS_CPU(dst_id)) {
1091739a20eSAndy Ritger         channel_type = UVM_CHANNEL_TYPE_GPU_TO_CPU;
1101739a20eSAndy Ritger     }
1111739a20eSAndy Ritger     else {
1121739a20eSAndy Ritger         UVM_ASSERT(uvm_id_equal(dst_id, gpu->id));
1131739a20eSAndy Ritger         channel_type = UVM_CHANNEL_TYPE_GPU_INTERNAL;
1141739a20eSAndy Ritger     }
1151739a20eSAndy Ritger 
1161739a20eSAndy Ritger     return uvm_push_begin(gpu->channel_manager,
1171739a20eSAndy Ritger                           channel_type,
1181739a20eSAndy Ritger                           push,
1191739a20eSAndy Ritger                           "Zero %s from %s VMA region [0x%lx, 0x%lx]",
1201739a20eSAndy Ritger                           uvm_va_space_processor_name(va_space, dst_id),
1211739a20eSAndy Ritger                           uvm_va_space_processor_name(va_space, gpu->id),
1221739a20eSAndy Ritger                           start,
1231739a20eSAndy Ritger                           outer);
1241739a20eSAndy Ritger }
1251739a20eSAndy Ritger 
1261739a20eSAndy Ritger // Create a new push to copy pages between src_id and dst_id
migrate_vma_copy_begin_push(uvm_va_space_t * va_space,uvm_processor_id_t dst_id,uvm_processor_id_t src_id,unsigned long start,unsigned long outer,uvm_push_t * push)1271739a20eSAndy Ritger static NV_STATUS migrate_vma_copy_begin_push(uvm_va_space_t *va_space,
1281739a20eSAndy Ritger                                              uvm_processor_id_t dst_id,
1291739a20eSAndy Ritger                                              uvm_processor_id_t src_id,
1301739a20eSAndy Ritger                                              unsigned long start,
1311739a20eSAndy Ritger                                              unsigned long outer,
1321739a20eSAndy Ritger                                              uvm_push_t *push)
1331739a20eSAndy Ritger {
1341739a20eSAndy Ritger     uvm_channel_type_t channel_type;
1351739a20eSAndy Ritger     uvm_gpu_t *gpu;
1361739a20eSAndy Ritger 
1371739a20eSAndy Ritger     UVM_ASSERT_MSG(!uvm_id_equal(src_id, dst_id),
1381739a20eSAndy Ritger                    "Unexpected copy to self, processor %s\n",
1391739a20eSAndy Ritger                    uvm_va_space_processor_name(va_space, src_id));
1401739a20eSAndy Ritger 
1411739a20eSAndy Ritger     if (UVM_ID_IS_CPU(src_id)) {
1421739a20eSAndy Ritger         gpu = uvm_va_space_get_gpu(va_space, dst_id);
1431739a20eSAndy Ritger         channel_type = UVM_CHANNEL_TYPE_CPU_TO_GPU;
1441739a20eSAndy Ritger     }
1451739a20eSAndy Ritger     else if (UVM_ID_IS_CPU(dst_id)) {
1461739a20eSAndy Ritger         gpu = uvm_va_space_get_gpu(va_space, src_id);
1471739a20eSAndy Ritger         channel_type = UVM_CHANNEL_TYPE_GPU_TO_CPU;
1481739a20eSAndy Ritger     }
1491739a20eSAndy Ritger     else {
1501739a20eSAndy Ritger         // For GPU to GPU copies, prefer to "push" the data from the source as
1511739a20eSAndy Ritger         // that works better
1521739a20eSAndy Ritger         gpu = uvm_va_space_get_gpu(va_space, src_id);
1531739a20eSAndy Ritger 
1541739a20eSAndy Ritger         channel_type = UVM_CHANNEL_TYPE_GPU_TO_GPU;
1551739a20eSAndy Ritger     }
1561739a20eSAndy Ritger 
1571739a20eSAndy Ritger     // NUMA-enabled GPUs can copy to any other NUMA node in the system even if
1581739a20eSAndy Ritger     // P2P access has not been explicitly enabled (ie va_space->can_copy_from
1591739a20eSAndy Ritger     // is not set).
160eb5c7665SAndy Ritger     if (!gpu->mem_info.numa.enabled) {
1611739a20eSAndy Ritger         UVM_ASSERT_MSG(uvm_processor_mask_test(&va_space->can_copy_from[uvm_id_value(gpu->id)], dst_id),
1621739a20eSAndy Ritger                        "GPU %s dst %s src %s\n",
1631739a20eSAndy Ritger                        uvm_va_space_processor_name(va_space, gpu->id),
1641739a20eSAndy Ritger                        uvm_va_space_processor_name(va_space, dst_id),
1651739a20eSAndy Ritger                        uvm_va_space_processor_name(va_space, src_id));
1661739a20eSAndy Ritger         UVM_ASSERT_MSG(uvm_processor_mask_test(&va_space->can_copy_from[uvm_id_value(gpu->id)], src_id),
1671739a20eSAndy Ritger                        "GPU %s dst %s src %s\n",
1681739a20eSAndy Ritger                        uvm_va_space_processor_name(va_space, gpu->id),
1691739a20eSAndy Ritger                        uvm_va_space_processor_name(va_space, dst_id),
1701739a20eSAndy Ritger                        uvm_va_space_processor_name(va_space, src_id));
1711739a20eSAndy Ritger     }
1721739a20eSAndy Ritger 
1731739a20eSAndy Ritger     if (channel_type == UVM_CHANNEL_TYPE_GPU_TO_GPU) {
1741739a20eSAndy Ritger         uvm_gpu_t *dst_gpu = uvm_va_space_get_gpu(va_space, dst_id);
1751739a20eSAndy Ritger         return uvm_push_begin_gpu_to_gpu(gpu->channel_manager,
1761739a20eSAndy Ritger                                          dst_gpu,
1771739a20eSAndy Ritger                                          push,
1781739a20eSAndy Ritger                                          "Copy from %s to %s for VMA region [0x%lx, 0x%lx]",
1791739a20eSAndy Ritger                                          uvm_va_space_processor_name(va_space, src_id),
1801739a20eSAndy Ritger                                          uvm_va_space_processor_name(va_space, dst_id),
1811739a20eSAndy Ritger                                          start,
1821739a20eSAndy Ritger                                          outer);
1831739a20eSAndy Ritger     }
1841739a20eSAndy Ritger 
1851739a20eSAndy Ritger     return uvm_push_begin(gpu->channel_manager,
1861739a20eSAndy Ritger                           channel_type,
1871739a20eSAndy Ritger                           push,
1881739a20eSAndy Ritger                           "Copy from %s to %s for VMA region [0x%lx, 0x%lx]",
1891739a20eSAndy Ritger                           uvm_va_space_processor_name(va_space, src_id),
1901739a20eSAndy Ritger                           uvm_va_space_processor_name(va_space, dst_id),
1911739a20eSAndy Ritger                           start,
1921739a20eSAndy Ritger                           outer);
1931739a20eSAndy Ritger }
1941739a20eSAndy Ritger 
migrate_vma_compute_masks(struct vm_area_struct * vma,const unsigned long * src,migrate_vma_state_t * state)1951739a20eSAndy Ritger static void migrate_vma_compute_masks(struct vm_area_struct *vma, const unsigned long *src, migrate_vma_state_t *state)
1961739a20eSAndy Ritger {
1971739a20eSAndy Ritger     unsigned long i;
1981739a20eSAndy Ritger     const bool is_rw = vma->vm_flags & VM_WRITE;
1991739a20eSAndy Ritger     uvm_migrate_args_t *uvm_migrate_args = state->uvm_migrate_args;
2001739a20eSAndy Ritger     uvm_processor_id_t dst_id = uvm_migrate_args->dst_id;
2011739a20eSAndy Ritger 
2021739a20eSAndy Ritger     UVM_ASSERT(vma_is_anonymous(vma));
2031739a20eSAndy Ritger 
2041739a20eSAndy Ritger     bitmap_zero(state->populate_pages_mask, state->num_pages);
2051739a20eSAndy Ritger     bitmap_zero(state->allocation_failed_mask, state->num_pages);
2061739a20eSAndy Ritger     bitmap_zero(state->dst_resident_pages_mask, state->num_pages);
2071739a20eSAndy Ritger 
2081739a20eSAndy Ritger     uvm_processor_mask_zero(&state->src_processors);
2091739a20eSAndy Ritger     state->num_populate_anon_pages = 0;
2101739a20eSAndy Ritger     state->dma.num_pages = 0;
2111739a20eSAndy Ritger 
2121739a20eSAndy Ritger     for (i = 0; i < state->num_pages; ++i) {
2131739a20eSAndy Ritger         uvm_processor_id_t src_id;
2141739a20eSAndy Ritger         struct page *src_page = NULL;
2151739a20eSAndy Ritger         int src_nid;
2161739a20eSAndy Ritger         uvm_gpu_t *src_gpu = NULL;
2171739a20eSAndy Ritger 
2181739a20eSAndy Ritger         // Skip pages that cannot be migrated
2191739a20eSAndy Ritger         if (!(src[i] & MIGRATE_PFN_MIGRATE)) {
2201739a20eSAndy Ritger             // This can happen in two cases :
2211739a20eSAndy Ritger             // - Page is populated but can't be migrated.
2221739a20eSAndy Ritger             // - Page isn't populated
2231739a20eSAndy Ritger             // In both the above cases, treat the page as failing migration and
2241739a20eSAndy Ritger             // populate with get_user_pages.
2251739a20eSAndy Ritger             if (!(src[i] & MIGRATE_PFN_VALID))
2261739a20eSAndy Ritger                 __set_bit(i, state->populate_pages_mask);
2271739a20eSAndy Ritger 
2281739a20eSAndy Ritger             continue;
2291739a20eSAndy Ritger         }
2301739a20eSAndy Ritger 
2311739a20eSAndy Ritger         src_page = migrate_pfn_to_page(src[i]);
2321739a20eSAndy Ritger         if (!src_page) {
2331739a20eSAndy Ritger             if (is_rw) {
2341739a20eSAndy Ritger                 // Populate PROT_WRITE vmas in migrate_vma so we can use the
2351739a20eSAndy Ritger                 // GPU's copy engines
2361739a20eSAndy Ritger                 if (state->num_populate_anon_pages++ == 0)
2371739a20eSAndy Ritger                     bitmap_zero(state->processors[uvm_id_value(dst_id)].page_mask, state->num_pages);
2381739a20eSAndy Ritger 
2391739a20eSAndy Ritger                 __set_bit(i, state->processors[uvm_id_value(dst_id)].page_mask);
2401739a20eSAndy Ritger             }
2411739a20eSAndy Ritger             else {
2421739a20eSAndy Ritger                 // PROT_NONE vmas cannot be populated. PROT_READ anonymous vmas
2431739a20eSAndy Ritger                 // are populated using the zero page. In order to match this
2441739a20eSAndy Ritger                 // behavior, we tell the caller to populate using
2451739a20eSAndy Ritger                 // get_user_pages.
2461739a20eSAndy Ritger                 __set_bit(i, state->populate_pages_mask);
2471739a20eSAndy Ritger             }
2481739a20eSAndy Ritger 
2491739a20eSAndy Ritger             continue;
2501739a20eSAndy Ritger         }
2511739a20eSAndy Ritger 
2521739a20eSAndy Ritger         // Page is already mapped. Skip migration of this page if requested.
2531739a20eSAndy Ritger         if (uvm_migrate_args->skip_mapped) {
2541739a20eSAndy Ritger             __set_bit(i, state->populate_pages_mask);
2551739a20eSAndy Ritger             continue;
2561739a20eSAndy Ritger         }
2571739a20eSAndy Ritger 
2581739a20eSAndy Ritger         src_nid = page_to_nid(src_page);
2591739a20eSAndy Ritger 
2601739a20eSAndy Ritger         // Already at destination
2611739a20eSAndy Ritger         if (src_nid == uvm_migrate_args->dst_node_id) {
2621739a20eSAndy Ritger             __set_bit(i, state->dst_resident_pages_mask);
2631739a20eSAndy Ritger             continue;
2641739a20eSAndy Ritger         }
2651739a20eSAndy Ritger 
2661739a20eSAndy Ritger         // Already resident on a CPU node, don't move
2671739a20eSAndy Ritger         if (UVM_ID_IS_CPU(dst_id) && node_state(src_nid, N_CPU)) {
2681739a20eSAndy Ritger             __set_bit(i, state->dst_resident_pages_mask);
2691739a20eSAndy Ritger             continue;
2701739a20eSAndy Ritger         }
2711739a20eSAndy Ritger 
272eb5c7665SAndy Ritger         src_gpu = uvm_va_space_find_gpu_with_memory_node_id(uvm_migrate_args->va_space, src_nid);
2731739a20eSAndy Ritger 
2741739a20eSAndy Ritger         // Already resident on a node with no CPUs that doesn't belong to a
2751739a20eSAndy Ritger         // GPU, don't move
2761739a20eSAndy Ritger         if (UVM_ID_IS_CPU(dst_id) && !src_gpu) {
2771739a20eSAndy Ritger             __set_bit(i, state->dst_resident_pages_mask);
2781739a20eSAndy Ritger             continue;
2791739a20eSAndy Ritger         }
2801739a20eSAndy Ritger 
2811739a20eSAndy Ritger         // TODO: Bug 2449272: Implement non-P2P copies. All systems that hit
2821739a20eSAndy Ritger         // this path have P2P copy support between all GPUs in the system, but
2831739a20eSAndy Ritger         // it could change in the future.
2841739a20eSAndy Ritger 
2851739a20eSAndy Ritger         if (src_gpu)
2861739a20eSAndy Ritger             src_id = src_gpu->id;
2871739a20eSAndy Ritger         else
2881739a20eSAndy Ritger             src_id = UVM_ID_CPU;
2891739a20eSAndy Ritger 
2901739a20eSAndy Ritger         if (!uvm_processor_mask_test_and_set(&state->src_processors, src_id))
2911739a20eSAndy Ritger             bitmap_zero(state->processors[uvm_id_value(src_id)].page_mask, state->num_pages);
2921739a20eSAndy Ritger 
2931739a20eSAndy Ritger         __set_bit(i, state->processors[uvm_id_value(src_id)].page_mask);
2941739a20eSAndy Ritger     }
2951739a20eSAndy Ritger }
2961739a20eSAndy Ritger 
migrate_vma_alloc_page(migrate_vma_state_t * state)2971739a20eSAndy Ritger static struct page *migrate_vma_alloc_page(migrate_vma_state_t *state)
2981739a20eSAndy Ritger {
2991739a20eSAndy Ritger     struct page *dst_page;
3001739a20eSAndy Ritger     uvm_migrate_args_t *uvm_migrate_args = state->uvm_migrate_args;
3011739a20eSAndy Ritger     uvm_va_space_t *va_space = uvm_migrate_args->va_space;
3021739a20eSAndy Ritger 
3031739a20eSAndy Ritger     if (uvm_enable_builtin_tests && atomic_dec_if_positive(&va_space->test.migrate_vma_allocation_fail_nth) == 0) {
3041739a20eSAndy Ritger         dst_page = NULL;
3051739a20eSAndy Ritger     }
3061739a20eSAndy Ritger     else {
3071739a20eSAndy Ritger         dst_page = alloc_pages_node(uvm_migrate_args->dst_node_id, g_migrate_vma_gfp_flags, 0);
3081739a20eSAndy Ritger 
3091739a20eSAndy Ritger         // TODO: Bug 2399573: Linux commit
3101739a20eSAndy Ritger         // 183f6371aac2a5496a8ef2b0b0a68562652c3cdb introduced a bug that makes
3111739a20eSAndy Ritger         // __GFP_THISNODE not always be honored (this was later fixed in commit
3121739a20eSAndy Ritger         // 7810e6781e0fcbca78b91cf65053f895bf59e85f). Therefore, we verify
3131739a20eSAndy Ritger         // whether the flag has been honored and abort the allocation,
3141739a20eSAndy Ritger         // otherwise. Remove this check when the fix is deployed on all
3151739a20eSAndy Ritger         // production systems.
3161739a20eSAndy Ritger         if (dst_page && page_to_nid(dst_page) != uvm_migrate_args->dst_node_id) {
3171739a20eSAndy Ritger             __free_page(dst_page);
3181739a20eSAndy Ritger             dst_page = NULL;
3191739a20eSAndy Ritger         }
3201739a20eSAndy Ritger     }
3211739a20eSAndy Ritger 
3221739a20eSAndy Ritger     return dst_page;
3231739a20eSAndy Ritger }
3241739a20eSAndy Ritger 
migrate_vma_populate_anon_pages(struct vm_area_struct * vma,unsigned long * dst,unsigned long start,unsigned long outer,migrate_vma_state_t * state)3251739a20eSAndy Ritger static NV_STATUS migrate_vma_populate_anon_pages(struct vm_area_struct *vma,
3261739a20eSAndy Ritger                                                  unsigned long *dst,
3271739a20eSAndy Ritger                                                  unsigned long start,
3281739a20eSAndy Ritger                                                  unsigned long outer,
3291739a20eSAndy Ritger                                                  migrate_vma_state_t *state)
3301739a20eSAndy Ritger {
3311739a20eSAndy Ritger     NV_STATUS status = NV_OK;
3321739a20eSAndy Ritger     uvm_migrate_args_t *uvm_migrate_args = state->uvm_migrate_args;
3331739a20eSAndy Ritger     uvm_processor_id_t dst_id = uvm_migrate_args->dst_id;
3341739a20eSAndy Ritger     unsigned long *page_mask = state->processors[uvm_id_value(dst_id)].page_mask;
3351739a20eSAndy Ritger     uvm_gpu_t *copying_gpu = NULL;
3361739a20eSAndy Ritger     uvm_va_space_t *va_space = uvm_migrate_args->va_space;
3371739a20eSAndy Ritger     uvm_push_t push;
3381739a20eSAndy Ritger     unsigned long i;
3391739a20eSAndy Ritger 
3401739a20eSAndy Ritger     // Nothing to do
3411739a20eSAndy Ritger     if (state->num_populate_anon_pages == 0)
3421739a20eSAndy Ritger         return NV_OK;
3431739a20eSAndy Ritger 
3441739a20eSAndy Ritger     UVM_ASSERT(state->num_populate_anon_pages == bitmap_weight(page_mask, state->num_pages));
3451739a20eSAndy Ritger 
3461739a20eSAndy Ritger     for_each_set_bit(i, page_mask, state->num_pages) {
3471739a20eSAndy Ritger         uvm_gpu_address_t dst_address;
3481739a20eSAndy Ritger         struct page *dst_page;
3491739a20eSAndy Ritger 
3501739a20eSAndy Ritger         dst_page = migrate_vma_alloc_page(state);
3511739a20eSAndy Ritger         if (!dst_page) {
3521739a20eSAndy Ritger             __set_bit(i, state->allocation_failed_mask);
3531739a20eSAndy Ritger             continue;
3541739a20eSAndy Ritger         }
3551739a20eSAndy Ritger 
3561739a20eSAndy Ritger         if (!copying_gpu) {
3571739a20eSAndy Ritger             // Try to get a GPU attached to the node being populated. If there
3581739a20eSAndy Ritger             // is none, use any of the GPUs registered in the VA space.
3591739a20eSAndy Ritger             if (UVM_ID_IS_CPU(dst_id)) {
3601739a20eSAndy Ritger                 copying_gpu = uvm_va_space_find_first_gpu_attached_to_cpu_node(va_space, uvm_migrate_args->dst_node_id);
3611739a20eSAndy Ritger                 if (!copying_gpu)
3621739a20eSAndy Ritger                     copying_gpu = uvm_va_space_find_first_gpu(va_space);
3631739a20eSAndy Ritger             }
3641739a20eSAndy Ritger             else {
3651739a20eSAndy Ritger                 copying_gpu = uvm_va_space_get_gpu(va_space, dst_id);
3661739a20eSAndy Ritger             }
3671739a20eSAndy Ritger 
3681739a20eSAndy Ritger             UVM_ASSERT(copying_gpu);
3691739a20eSAndy Ritger 
3701739a20eSAndy Ritger             status = migrate_vma_zero_begin_push(va_space, dst_id, copying_gpu, start, outer - 1, &push);
3711739a20eSAndy Ritger             if (status != NV_OK) {
3721739a20eSAndy Ritger                 __free_page(dst_page);
3731739a20eSAndy Ritger                 return status;
3741739a20eSAndy Ritger             }
3751739a20eSAndy Ritger         }
3761739a20eSAndy Ritger         else {
3771739a20eSAndy Ritger             uvm_push_set_flag(&push, UVM_PUSH_FLAG_CE_NEXT_PIPELINED);
3781739a20eSAndy Ritger         }
3791739a20eSAndy Ritger 
3801739a20eSAndy Ritger         status = migrate_vma_page_copy_address(dst_page, i, dst_id, copying_gpu, state, &dst_address);
3811739a20eSAndy Ritger         if (status != NV_OK) {
3821739a20eSAndy Ritger             __free_page(dst_page);
3831739a20eSAndy Ritger             break;
3841739a20eSAndy Ritger         }
3851739a20eSAndy Ritger 
3861739a20eSAndy Ritger         lock_page(dst_page);
3871739a20eSAndy Ritger 
3881739a20eSAndy Ritger         // We'll push one membar later for all memsets in this loop
3891739a20eSAndy Ritger         uvm_push_set_flag(&push, UVM_PUSH_FLAG_NEXT_MEMBAR_NONE);
3901739a20eSAndy Ritger         copying_gpu->parent->ce_hal->memset_8(&push, dst_address, 0, PAGE_SIZE);
3911739a20eSAndy Ritger 
3921739a20eSAndy Ritger         dst[i] = migrate_pfn(page_to_pfn(dst_page));
3931739a20eSAndy Ritger     }
3941739a20eSAndy Ritger 
3951739a20eSAndy Ritger     if (copying_gpu) {
3961739a20eSAndy Ritger         NV_STATUS tracker_status;
3971739a20eSAndy Ritger 
3981739a20eSAndy Ritger         uvm_push_end(&push);
3991739a20eSAndy Ritger 
4001739a20eSAndy Ritger         tracker_status = uvm_tracker_add_push_safe(&state->tracker, &push);
4011739a20eSAndy Ritger         if (status == NV_OK)
4021739a20eSAndy Ritger             status = tracker_status;
4031739a20eSAndy Ritger     }
4041739a20eSAndy Ritger 
4051739a20eSAndy Ritger     return status;
4061739a20eSAndy Ritger }
4071739a20eSAndy Ritger 
migrate_vma_copy_pages_from(struct vm_area_struct * vma,const unsigned long * src,unsigned long * dst,unsigned long start,unsigned long outer,uvm_processor_id_t src_id,migrate_vma_state_t * state)4081739a20eSAndy Ritger static NV_STATUS migrate_vma_copy_pages_from(struct vm_area_struct *vma,
4091739a20eSAndy Ritger                                              const unsigned long *src,
4101739a20eSAndy Ritger                                              unsigned long *dst,
4111739a20eSAndy Ritger                                              unsigned long start,
4121739a20eSAndy Ritger                                              unsigned long outer,
4131739a20eSAndy Ritger                                              uvm_processor_id_t src_id,
4141739a20eSAndy Ritger                                              migrate_vma_state_t *state)
4151739a20eSAndy Ritger {
4161739a20eSAndy Ritger     NV_STATUS status = NV_OK;
4171739a20eSAndy Ritger     uvm_push_t push;
4181739a20eSAndy Ritger     unsigned long i;
4191739a20eSAndy Ritger     uvm_gpu_t *copying_gpu = NULL;
4201739a20eSAndy Ritger     uvm_migrate_args_t *uvm_migrate_args = state->uvm_migrate_args;
4211739a20eSAndy Ritger     uvm_processor_id_t dst_id = uvm_migrate_args->dst_id;
4221739a20eSAndy Ritger     unsigned long *page_mask = state->processors[uvm_id_value(src_id)].page_mask;
4231739a20eSAndy Ritger     uvm_va_space_t *va_space = uvm_migrate_args->va_space;
4241739a20eSAndy Ritger 
4251739a20eSAndy Ritger     UVM_ASSERT(!bitmap_empty(page_mask, state->num_pages));
4261739a20eSAndy Ritger 
4271739a20eSAndy Ritger     for_each_set_bit(i, page_mask, state->num_pages) {
4281739a20eSAndy Ritger         uvm_gpu_address_t src_address;
4291739a20eSAndy Ritger         uvm_gpu_address_t dst_address;
4301739a20eSAndy Ritger         struct page *src_page = migrate_pfn_to_page(src[i]);
4311739a20eSAndy Ritger         struct page *dst_page;
4321739a20eSAndy Ritger 
4331739a20eSAndy Ritger         UVM_ASSERT(src[i] & MIGRATE_PFN_VALID);
4341739a20eSAndy Ritger         UVM_ASSERT(src_page);
4351739a20eSAndy Ritger 
4361739a20eSAndy Ritger         dst_page = migrate_vma_alloc_page(state);
4371739a20eSAndy Ritger         if (!dst_page) {
4381739a20eSAndy Ritger             __set_bit(i, state->allocation_failed_mask);
4391739a20eSAndy Ritger             continue;
4401739a20eSAndy Ritger         }
4411739a20eSAndy Ritger 
4421739a20eSAndy Ritger         if (!copying_gpu) {
4431739a20eSAndy Ritger             status = migrate_vma_copy_begin_push(va_space, dst_id, src_id, start, outer - 1, &push);
4441739a20eSAndy Ritger             if (status != NV_OK) {
4451739a20eSAndy Ritger                 __free_page(dst_page);
4461739a20eSAndy Ritger                 return status;
4471739a20eSAndy Ritger             }
4481739a20eSAndy Ritger 
4491739a20eSAndy Ritger             copying_gpu = uvm_push_get_gpu(&push);
4501739a20eSAndy Ritger         }
4511739a20eSAndy Ritger         else {
4521739a20eSAndy Ritger             uvm_push_set_flag(&push, UVM_PUSH_FLAG_CE_NEXT_PIPELINED);
4531739a20eSAndy Ritger         }
4541739a20eSAndy Ritger 
4551739a20eSAndy Ritger         // We don't have a case where both src and dst use the SYS aperture, so
4561739a20eSAndy Ritger         // the second call can't overwrite a dma addr set up by the first call.
4571739a20eSAndy Ritger         status = migrate_vma_page_copy_address(src_page, i, src_id, copying_gpu, state, &src_address);
4581739a20eSAndy Ritger         if (status == NV_OK)
4591739a20eSAndy Ritger             status = migrate_vma_page_copy_address(dst_page, i, dst_id, copying_gpu, state, &dst_address);
4601739a20eSAndy Ritger 
4611739a20eSAndy Ritger         if (status != NV_OK) {
4621739a20eSAndy Ritger             __free_page(dst_page);
4631739a20eSAndy Ritger             break;
4641739a20eSAndy Ritger         }
4651739a20eSAndy Ritger 
4661739a20eSAndy Ritger         lock_page(dst_page);
4671739a20eSAndy Ritger 
4681739a20eSAndy Ritger         // We'll push one membar later for all copies in this loop
4691739a20eSAndy Ritger         uvm_push_set_flag(&push, UVM_PUSH_FLAG_NEXT_MEMBAR_NONE);
4701739a20eSAndy Ritger         copying_gpu->parent->ce_hal->memcopy(&push, dst_address, src_address, PAGE_SIZE);
4711739a20eSAndy Ritger 
4721739a20eSAndy Ritger         dst[i] = migrate_pfn(page_to_pfn(dst_page));
4731739a20eSAndy Ritger     }
4741739a20eSAndy Ritger 
4751739a20eSAndy Ritger     // TODO: Bug 1766424: If the destination is a GPU and the copy was done by
4761739a20eSAndy Ritger     //       that GPU, use a GPU-local membar if no peer nor the CPU can
4771739a20eSAndy Ritger     //       currently map this page. When peer access gets enabled, do a
4781739a20eSAndy Ritger     //       MEMBAR_SYS at that point.
4791739a20eSAndy Ritger     if (copying_gpu) {
4801739a20eSAndy Ritger         NV_STATUS tracker_status;
4811739a20eSAndy Ritger 
4821739a20eSAndy Ritger         uvm_push_end(&push);
4831739a20eSAndy Ritger 
4841739a20eSAndy Ritger         tracker_status = uvm_tracker_add_push_safe(&state->tracker, &push);
4851739a20eSAndy Ritger         if (status == NV_OK)
4861739a20eSAndy Ritger             status = tracker_status;
4871739a20eSAndy Ritger     }
4881739a20eSAndy Ritger 
4891739a20eSAndy Ritger     return status;
4901739a20eSAndy Ritger }
4911739a20eSAndy Ritger 
migrate_vma_copy_pages(struct vm_area_struct * vma,const unsigned long * src,unsigned long * dst,unsigned long start,unsigned long outer,migrate_vma_state_t * state)4921739a20eSAndy Ritger static NV_STATUS migrate_vma_copy_pages(struct vm_area_struct *vma,
4931739a20eSAndy Ritger                                         const unsigned long *src,
4941739a20eSAndy Ritger                                         unsigned long *dst,
4951739a20eSAndy Ritger                                         unsigned long start,
4961739a20eSAndy Ritger                                         unsigned long outer,
4971739a20eSAndy Ritger                                         migrate_vma_state_t *state)
4981739a20eSAndy Ritger {
4991739a20eSAndy Ritger     uvm_processor_id_t src_id;
5001739a20eSAndy Ritger 
5011739a20eSAndy Ritger     for_each_id_in_mask(src_id, &state->src_processors) {
5021739a20eSAndy Ritger         NV_STATUS status = migrate_vma_copy_pages_from(vma, src, dst, start, outer, src_id, state);
5031739a20eSAndy Ritger         if (status != NV_OK)
5041739a20eSAndy Ritger             return status;
5051739a20eSAndy Ritger     }
5061739a20eSAndy Ritger 
5071739a20eSAndy Ritger     return NV_OK;
5081739a20eSAndy Ritger }
5091739a20eSAndy Ritger 
migrate_vma_cleanup_pages(unsigned long * dst,unsigned long npages)510337e28efSBernhard Stoeckner void migrate_vma_cleanup_pages(unsigned long *dst, unsigned long npages)
511337e28efSBernhard Stoeckner {
512337e28efSBernhard Stoeckner     unsigned long i;
513337e28efSBernhard Stoeckner 
514337e28efSBernhard Stoeckner     for (i = 0; i < npages; i++) {
515337e28efSBernhard Stoeckner         struct page *dst_page = migrate_pfn_to_page(dst[i]);
516337e28efSBernhard Stoeckner 
517337e28efSBernhard Stoeckner         if (!dst_page)
518337e28efSBernhard Stoeckner             continue;
519337e28efSBernhard Stoeckner 
520337e28efSBernhard Stoeckner         unlock_page(dst_page);
521337e28efSBernhard Stoeckner         __free_page(dst_page);
522337e28efSBernhard Stoeckner         dst[i] = 0;
523337e28efSBernhard Stoeckner     }
524337e28efSBernhard Stoeckner }
525337e28efSBernhard Stoeckner 
uvm_migrate_vma_alloc_and_copy(struct migrate_vma * args,migrate_vma_state_t * state)5261739a20eSAndy Ritger void uvm_migrate_vma_alloc_and_copy(struct migrate_vma *args, migrate_vma_state_t *state)
5271739a20eSAndy Ritger {
5281739a20eSAndy Ritger     struct vm_area_struct *vma = args->vma;
5291739a20eSAndy Ritger     unsigned long start = args->start;
5301739a20eSAndy Ritger     unsigned long outer = args->end;
5311739a20eSAndy Ritger     NV_STATUS tracker_status;
5321739a20eSAndy Ritger 
5331739a20eSAndy Ritger     uvm_tracker_init(&state->tracker);
5341739a20eSAndy Ritger 
5351739a20eSAndy Ritger     state->num_pages = (outer - start) / PAGE_SIZE;
5361739a20eSAndy Ritger     state->status = NV_OK;
5371739a20eSAndy Ritger 
5381739a20eSAndy Ritger     migrate_vma_compute_masks(vma, args->src, state);
5391739a20eSAndy Ritger 
5401739a20eSAndy Ritger     state->status = migrate_vma_populate_anon_pages(vma, args->dst, start, outer, state);
5411739a20eSAndy Ritger 
5421739a20eSAndy Ritger     if (state->status == NV_OK)
5431739a20eSAndy Ritger         state->status = migrate_vma_copy_pages(vma, args->src, args->dst, start, outer, state);
5441739a20eSAndy Ritger 
5451739a20eSAndy Ritger     // Wait for tracker since all copies must have completed before returning
5461739a20eSAndy Ritger     tracker_status = uvm_tracker_wait_deinit(&state->tracker);
5471739a20eSAndy Ritger 
5481739a20eSAndy Ritger     if (state->status == NV_OK)
5491739a20eSAndy Ritger         state->status = tracker_status;
550337e28efSBernhard Stoeckner 
551337e28efSBernhard Stoeckner     // Mark all pages as not migrating if we're failing
552337e28efSBernhard Stoeckner     if (state->status != NV_OK)
553337e28efSBernhard Stoeckner         migrate_vma_cleanup_pages(args->dst, state->num_pages);
5541739a20eSAndy Ritger }
5551739a20eSAndy Ritger 
uvm_migrate_vma_alloc_and_copy_helper(struct vm_area_struct * vma,const unsigned long * src,unsigned long * dst,unsigned long start,unsigned long end,void * private)5561739a20eSAndy Ritger void uvm_migrate_vma_alloc_and_copy_helper(struct vm_area_struct *vma,
5571739a20eSAndy Ritger                                 const unsigned long *src,
5581739a20eSAndy Ritger                                 unsigned long *dst,
5591739a20eSAndy Ritger                                 unsigned long start,
5601739a20eSAndy Ritger                                 unsigned long end,
5611739a20eSAndy Ritger                                 void *private)
5621739a20eSAndy Ritger {
5631739a20eSAndy Ritger     struct migrate_vma args =
5641739a20eSAndy Ritger     {
5651739a20eSAndy Ritger         .vma = vma,
5661739a20eSAndy Ritger         .dst = dst,
5671739a20eSAndy Ritger         .src = (unsigned long *) src,
5681739a20eSAndy Ritger         .start = start,
5691739a20eSAndy Ritger         .end = end,
5701739a20eSAndy Ritger     };
5711739a20eSAndy Ritger 
5721739a20eSAndy Ritger     uvm_migrate_vma_alloc_and_copy(&args, (migrate_vma_state_t *) private);
5731739a20eSAndy Ritger }
5741739a20eSAndy Ritger 
uvm_migrate_vma_finalize_and_map(struct migrate_vma * args,migrate_vma_state_t * state)5751739a20eSAndy Ritger void uvm_migrate_vma_finalize_and_map(struct migrate_vma *args, migrate_vma_state_t *state)
5761739a20eSAndy Ritger {
5771739a20eSAndy Ritger     unsigned long i;
5781739a20eSAndy Ritger 
5791739a20eSAndy Ritger     for (i = 0; i < state->num_pages; i++) {
5801739a20eSAndy Ritger         bool needs_touch = false;
5811739a20eSAndy Ritger         uvm_migrate_args_t *uvm_migrate_args = state->uvm_migrate_args;
5821739a20eSAndy Ritger 
5831739a20eSAndy Ritger         // The page was successfully migrated.
5841739a20eSAndy Ritger         if (args->src[i] & MIGRATE_PFN_MIGRATE) {
5851739a20eSAndy Ritger             // Touch if requested since population of these pages won't be tried
5861739a20eSAndy Ritger             // later.
5871739a20eSAndy Ritger             needs_touch = true;
5881739a20eSAndy Ritger         }
5891739a20eSAndy Ritger         else {
5901739a20eSAndy Ritger             // The page was not migrated. This can happen for two reasons.
5911739a20eSAndy Ritger             //
5921739a20eSAndy Ritger             // 1. Page is already resident at the destination.
5931739a20eSAndy Ritger             // 2. Page failed migration because the page state could not be
5941739a20eSAndy Ritger             // migrated by the kernel.
5951739a20eSAndy Ritger             //
5961739a20eSAndy Ritger             // So, only set the corresponding populate_pages bit if both the
5971739a20eSAndy Ritger             // following conditions are true.
5981739a20eSAndy Ritger             //
5991739a20eSAndy Ritger             // 1.Trying to populate pages (with gup) which are already resident
6001739a20eSAndy Ritger             // at the destination is wasteful but usually harmless except in the
6011739a20eSAndy Ritger             // PROT_NONE case. gup returns NV_ERR_INVALID_ADDRESS for such pages
6021739a20eSAndy Ritger             // and will incorrectly lead to API migration failures even though
6031739a20eSAndy Ritger             // migration worked as expected.
6041739a20eSAndy Ritger             //
6051739a20eSAndy Ritger             // 2. Migration failure was not because of allocation failure in
6061739a20eSAndy Ritger             // uvm_migrate_vma_finalize_and_map() since such failures would be
6071739a20eSAndy Ritger             // indicated in allocation_failed_mask. Failures other than
6081739a20eSAndy Ritger             // allocation failures likely means that the page is populated
6091739a20eSAndy Ritger             // somewhere. So, set the corresponding bit in populate_pages_mask.
6101739a20eSAndy Ritger             if (test_bit(i, state->dst_resident_pages_mask)) {
6111739a20eSAndy Ritger 
6121739a20eSAndy Ritger                 // If touch was requested, pages in allocation_failed and
6131739a20eSAndy Ritger                 // populate_pages masks will be touched during population. But pages
6141739a20eSAndy Ritger                 // which are already resident at the destination need to be touched
6151739a20eSAndy Ritger                 // here since population isn't tried later for such pages.
6161739a20eSAndy Ritger                 needs_touch = true;
6171739a20eSAndy Ritger             }
6181739a20eSAndy Ritger             else if (!test_bit(i, state->allocation_failed_mask)) {
6191739a20eSAndy Ritger                 __set_bit(i, state->populate_pages_mask);
6201739a20eSAndy Ritger             }
6211739a20eSAndy Ritger         }
6221739a20eSAndy Ritger 
6231739a20eSAndy Ritger         // Touch if requested and needed.
6241739a20eSAndy Ritger         if (uvm_migrate_args->touch && needs_touch) {
6251739a20eSAndy Ritger             struct page *dst_page;
6261739a20eSAndy Ritger 
6271739a20eSAndy Ritger             UVM_ASSERT(args->dst[i] & MIGRATE_PFN_VALID);
6281739a20eSAndy Ritger 
6291739a20eSAndy Ritger             dst_page = migrate_pfn_to_page(args->dst[i]);
6301739a20eSAndy Ritger             UVM_ASSERT(dst_page);
6311739a20eSAndy Ritger             uvm_touch_page(dst_page);
6321739a20eSAndy Ritger         }
6331739a20eSAndy Ritger     }
6341739a20eSAndy Ritger 
6351739a20eSAndy Ritger     // Remove the IOMMU mappings created during the copy
6361739a20eSAndy Ritger     if (state->dma.num_pages > 0) {
6371739a20eSAndy Ritger 
6381739a20eSAndy Ritger         for_each_set_bit(i, state->dma.page_mask, state->num_pages)
639*91676d66SBernhard Stoeckner             uvm_parent_gpu_unmap_cpu_page(state->dma.addrs_gpus[i]->parent, state->dma.addrs[i]);
6401739a20eSAndy Ritger     }
6411739a20eSAndy Ritger 
6421739a20eSAndy Ritger     UVM_ASSERT(!bitmap_intersects(state->populate_pages_mask, state->allocation_failed_mask, state->num_pages));
6431739a20eSAndy Ritger }
6441739a20eSAndy Ritger 
uvm_migrate_vma_finalize_and_map_helper(struct vm_area_struct * vma,const unsigned long * src,const unsigned long * dst,unsigned long start,unsigned long end,void * private)6451739a20eSAndy Ritger void uvm_migrate_vma_finalize_and_map_helper(struct vm_area_struct *vma,
6461739a20eSAndy Ritger                                              const unsigned long *src,
6471739a20eSAndy Ritger                                              const unsigned long *dst,
6481739a20eSAndy Ritger                                              unsigned long start,
6491739a20eSAndy Ritger                                              unsigned long end,
6501739a20eSAndy Ritger                                              void *private)
6511739a20eSAndy Ritger {
6521739a20eSAndy Ritger     struct migrate_vma args =
6531739a20eSAndy Ritger     {
6541739a20eSAndy Ritger         .vma = vma,
6551739a20eSAndy Ritger         .dst = (unsigned long *) dst,
6561739a20eSAndy Ritger         .src = (unsigned long *) src,
6571739a20eSAndy Ritger         .start = start,
6581739a20eSAndy Ritger         .end = end,
6591739a20eSAndy Ritger     };
6601739a20eSAndy Ritger 
6611739a20eSAndy Ritger     uvm_migrate_vma_finalize_and_map(&args, (migrate_vma_state_t *) private);
6621739a20eSAndy Ritger }
6631739a20eSAndy Ritger 
nv_migrate_vma(struct migrate_vma * args,migrate_vma_state_t * state)6641739a20eSAndy Ritger static NV_STATUS nv_migrate_vma(struct migrate_vma *args, migrate_vma_state_t *state)
6651739a20eSAndy Ritger {
6661739a20eSAndy Ritger     int ret;
6671739a20eSAndy Ritger 
6681739a20eSAndy Ritger #if defined(CONFIG_MIGRATE_VMA_HELPER)
6691739a20eSAndy Ritger     static const struct migrate_vma_ops uvm_migrate_vma_ops =
6701739a20eSAndy Ritger     {
6711739a20eSAndy Ritger         .alloc_and_copy = uvm_migrate_vma_alloc_and_copy_helper,
6721739a20eSAndy Ritger         .finalize_and_map = uvm_migrate_vma_finalize_and_map_helper,
6731739a20eSAndy Ritger     };
6741739a20eSAndy Ritger 
6751739a20eSAndy Ritger     ret = migrate_vma(&uvm_migrate_vma_ops, args->vma, args->start, args->end, args->src, args->dst, state);
6761739a20eSAndy Ritger     if (ret < 0)
6771739a20eSAndy Ritger         return errno_to_nv_status(ret);
6781739a20eSAndy Ritger #else // CONFIG_MIGRATE_VMA_HELPER
6791739a20eSAndy Ritger 
6801739a20eSAndy Ritger #if defined(NV_MIGRATE_VMA_FLAGS_PRESENT)
6811739a20eSAndy Ritger     args->flags = MIGRATE_VMA_SELECT_SYSTEM;
6821739a20eSAndy Ritger #endif // NV_MIGRATE_VMA_FLAGS_PRESENT
6831739a20eSAndy Ritger 
6841739a20eSAndy Ritger     ret = migrate_vma_setup(args);
6851739a20eSAndy Ritger     if (ret < 0)
6861739a20eSAndy Ritger         return errno_to_nv_status(ret);
6871739a20eSAndy Ritger 
6881739a20eSAndy Ritger     uvm_migrate_vma_alloc_and_copy(args, state);
6891739a20eSAndy Ritger     if (state->status == NV_OK) {
6901739a20eSAndy Ritger         migrate_vma_pages(args);
6911739a20eSAndy Ritger         uvm_migrate_vma_finalize_and_map(args, state);
6921739a20eSAndy Ritger     }
6931739a20eSAndy Ritger 
6941739a20eSAndy Ritger     migrate_vma_finalize(args);
6951739a20eSAndy Ritger #endif // CONFIG_MIGRATE_VMA_HELPER
6961739a20eSAndy Ritger 
6971739a20eSAndy Ritger     return state->status;
6981739a20eSAndy Ritger }
6991739a20eSAndy Ritger 
migrate_pageable_vma_populate_mask(struct vm_area_struct * vma,unsigned long start,unsigned long outer,const unsigned long * mask,migrate_vma_state_t * state)7001739a20eSAndy Ritger static NV_STATUS migrate_pageable_vma_populate_mask(struct vm_area_struct *vma,
7011739a20eSAndy Ritger                                                     unsigned long start,
7021739a20eSAndy Ritger                                                     unsigned long outer,
7031739a20eSAndy Ritger                                                     const unsigned long *mask,
7041739a20eSAndy Ritger                                                     migrate_vma_state_t *state)
7051739a20eSAndy Ritger {
7061739a20eSAndy Ritger     const unsigned long num_pages = (outer - start) / PAGE_SIZE;
7071739a20eSAndy Ritger     unsigned long subregion_first = find_first_bit(mask, num_pages);
7081739a20eSAndy Ritger     uvm_migrate_args_t *uvm_migrate_args = state->uvm_migrate_args;
7091739a20eSAndy Ritger 
7101739a20eSAndy Ritger     while (subregion_first < num_pages) {
7111739a20eSAndy Ritger         NV_STATUS status;
7121739a20eSAndy Ritger         unsigned long subregion_outer = find_next_zero_bit(mask, num_pages, subregion_first + 1);
7131739a20eSAndy Ritger 
7141739a20eSAndy Ritger         status = uvm_populate_pageable_vma(vma,
7151739a20eSAndy Ritger                                            start + subregion_first * PAGE_SIZE,
7161739a20eSAndy Ritger                                            (subregion_outer - subregion_first) * PAGE_SIZE,
7171739a20eSAndy Ritger                                            0,
7181739a20eSAndy Ritger                                            uvm_migrate_args->touch,
7191739a20eSAndy Ritger                                            uvm_migrate_args->populate_permissions);
7201739a20eSAndy Ritger         if (status != NV_OK)
7211739a20eSAndy Ritger             return status;
7221739a20eSAndy Ritger 
7231739a20eSAndy Ritger         subregion_first = find_next_bit(mask, num_pages, subregion_outer + 1);
7241739a20eSAndy Ritger     }
7251739a20eSAndy Ritger 
7261739a20eSAndy Ritger     return NV_OK;
7271739a20eSAndy Ritger }
7281739a20eSAndy Ritger 
migrate_pageable_vma_migrate_mask(struct vm_area_struct * vma,unsigned long start,unsigned long outer,const unsigned long * mask,migrate_vma_state_t * state)7291739a20eSAndy Ritger static NV_STATUS migrate_pageable_vma_migrate_mask(struct vm_area_struct *vma,
7301739a20eSAndy Ritger                                                    unsigned long start,
7311739a20eSAndy Ritger                                                    unsigned long outer,
7321739a20eSAndy Ritger                                                    const unsigned long *mask,
7331739a20eSAndy Ritger                                                    migrate_vma_state_t *state)
7341739a20eSAndy Ritger {
7351739a20eSAndy Ritger     NV_STATUS status;
7361739a20eSAndy Ritger     const unsigned long num_pages = (outer - start) / PAGE_SIZE;
7371739a20eSAndy Ritger     unsigned long subregion_first = find_first_bit(mask, num_pages);
7381739a20eSAndy Ritger     uvm_migrate_args_t *uvm_migrate_args = state->uvm_migrate_args;
7391739a20eSAndy Ritger     struct migrate_vma args =
7401739a20eSAndy Ritger     {
7411739a20eSAndy Ritger         .vma = vma,
7421739a20eSAndy Ritger         .src = state->src_pfn_array,
7431739a20eSAndy Ritger         .dst = state->dst_pfn_array,
7441739a20eSAndy Ritger     };
7451739a20eSAndy Ritger 
7461739a20eSAndy Ritger     UVM_ASSERT(!uvm_migrate_args->skip_mapped);
7471739a20eSAndy Ritger 
7481739a20eSAndy Ritger     while (subregion_first < num_pages) {
7491739a20eSAndy Ritger         unsigned long subregion_outer = find_next_zero_bit(mask, num_pages, subregion_first + 1);
7501739a20eSAndy Ritger 
7511739a20eSAndy Ritger         args.start = start + subregion_first * PAGE_SIZE;
7521739a20eSAndy Ritger         args.end = start + subregion_outer * PAGE_SIZE;
7531739a20eSAndy Ritger 
7541739a20eSAndy Ritger         status = nv_migrate_vma(&args, state);
7551739a20eSAndy Ritger         if (status != NV_OK)
7561739a20eSAndy Ritger             return status;
7571739a20eSAndy Ritger 
7581739a20eSAndy Ritger         // We ignore allocation failure here as we are just retrying migration,
7591739a20eSAndy Ritger         // but pages must have already been populated by the caller
7601739a20eSAndy Ritger 
7611739a20eSAndy Ritger         subregion_first = find_next_bit(mask, num_pages, subregion_outer + 1);
7621739a20eSAndy Ritger     }
7631739a20eSAndy Ritger 
7641739a20eSAndy Ritger     return NV_OK;
7651739a20eSAndy Ritger }
7661739a20eSAndy Ritger 
migrate_pageable_vma_region(struct vm_area_struct * vma,unsigned long start,unsigned long outer,migrate_vma_state_t * state,unsigned long * next_addr)7671739a20eSAndy Ritger static NV_STATUS migrate_pageable_vma_region(struct vm_area_struct *vma,
7681739a20eSAndy Ritger                                              unsigned long start,
7691739a20eSAndy Ritger                                              unsigned long outer,
7701739a20eSAndy Ritger                                              migrate_vma_state_t *state,
7711739a20eSAndy Ritger                                              unsigned long *next_addr)
7721739a20eSAndy Ritger {
7731739a20eSAndy Ritger     NV_STATUS status;
7741739a20eSAndy Ritger     const unsigned long num_pages = (outer - start) / PAGE_SIZE;
7751739a20eSAndy Ritger     struct mm_struct *mm = vma->vm_mm;
7761739a20eSAndy Ritger     uvm_migrate_args_t *uvm_migrate_args = state->uvm_migrate_args;
7771739a20eSAndy Ritger     struct migrate_vma args =
7781739a20eSAndy Ritger     {
7791739a20eSAndy Ritger         .vma = vma,
7801739a20eSAndy Ritger         .src = state->src_pfn_array,
7811739a20eSAndy Ritger         .dst = state->dst_pfn_array,
7821739a20eSAndy Ritger         .start = start,
7831739a20eSAndy Ritger         .end = outer,
7841739a20eSAndy Ritger     };
7851739a20eSAndy Ritger 
7861739a20eSAndy Ritger     UVM_ASSERT(PAGE_ALIGNED(start));
7871739a20eSAndy Ritger     UVM_ASSERT(PAGE_ALIGNED(outer));
7881739a20eSAndy Ritger     UVM_ASSERT(start < outer);
7891739a20eSAndy Ritger     UVM_ASSERT(start >= vma->vm_start);
7901739a20eSAndy Ritger     UVM_ASSERT(outer <= vma->vm_end);
7911739a20eSAndy Ritger     UVM_ASSERT(outer - start <= UVM_MIGRATE_VMA_MAX_SIZE);
7921739a20eSAndy Ritger     uvm_assert_mmap_lock_locked(mm);
7931739a20eSAndy Ritger     uvm_assert_rwsem_locked(&uvm_migrate_args->va_space->lock);
7941739a20eSAndy Ritger 
7951739a20eSAndy Ritger     status = nv_migrate_vma(&args, state);
7961739a20eSAndy Ritger     if (status != NV_OK)
7971739a20eSAndy Ritger         return status;
7981739a20eSAndy Ritger 
7991739a20eSAndy Ritger     // Save the returned page masks because they can be overwritten by
8001739a20eSAndy Ritger     // migrate_pageable_vma_migrate_mask().
8011739a20eSAndy Ritger     bitmap_copy(state->scratch1_mask, state->populate_pages_mask, num_pages);
8021739a20eSAndy Ritger     bitmap_copy(state->scratch2_mask, state->allocation_failed_mask, num_pages);
8031739a20eSAndy Ritger 
8041739a20eSAndy Ritger     if (!bitmap_empty(state->scratch1_mask, state->num_pages)) {
8051739a20eSAndy Ritger         // Populate pages using get_user_pages
8061739a20eSAndy Ritger         status = migrate_pageable_vma_populate_mask(vma, start, outer, state->scratch1_mask, state);
8071739a20eSAndy Ritger         if (status != NV_OK)
8081739a20eSAndy Ritger             return status;
8091739a20eSAndy Ritger 
8101739a20eSAndy Ritger         if (!uvm_migrate_args->skip_mapped) {
8111739a20eSAndy Ritger             status = migrate_pageable_vma_migrate_mask(vma, start, outer, state->scratch1_mask, state);
8121739a20eSAndy Ritger             if (status != NV_OK)
8131739a20eSAndy Ritger                 return status;
8141739a20eSAndy Ritger         }
8151739a20eSAndy Ritger     }
8161739a20eSAndy Ritger 
8171739a20eSAndy Ritger     // There is no need to copy the masks again after the migration is retried.
8181739a20eSAndy Ritger     // We ignore the allocation_failed, populate_pages and dst_resident_pages
8191739a20eSAndy Ritger     // masks set by the retried migration.
8201739a20eSAndy Ritger 
8211739a20eSAndy Ritger     if (!bitmap_empty(state->scratch2_mask, state->num_pages)) {
8221739a20eSAndy Ritger         // If the destination is the CPU, signal user-space to retry with a
8231739a20eSAndy Ritger         // different node. Otherwise, just try to populate anywhere in the
8241739a20eSAndy Ritger         // system
825337e28efSBernhard Stoeckner         if (UVM_ID_IS_CPU(uvm_migrate_args->dst_id) && !uvm_migrate_args->populate_on_cpu_alloc_failures) {
8261739a20eSAndy Ritger             *next_addr = start + find_first_bit(state->scratch2_mask, num_pages) * PAGE_SIZE;
8271739a20eSAndy Ritger             return NV_ERR_MORE_PROCESSING_REQUIRED;
8281739a20eSAndy Ritger         }
8291739a20eSAndy Ritger         else {
8301739a20eSAndy Ritger             status = migrate_pageable_vma_populate_mask(vma, start, outer, state->scratch2_mask, state);
8311739a20eSAndy Ritger             if (status != NV_OK)
8321739a20eSAndy Ritger                 return status;
8331739a20eSAndy Ritger         }
8341739a20eSAndy Ritger     }
8351739a20eSAndy Ritger 
8361739a20eSAndy Ritger     return NV_OK;
8371739a20eSAndy Ritger }
8381739a20eSAndy Ritger 
uvm_test_skip_migrate_vma(UVM_TEST_SKIP_MIGRATE_VMA_PARAMS * params,struct file * filp)839*91676d66SBernhard Stoeckner NV_STATUS uvm_test_skip_migrate_vma(UVM_TEST_SKIP_MIGRATE_VMA_PARAMS *params, struct file *filp)
840*91676d66SBernhard Stoeckner {
841*91676d66SBernhard Stoeckner     uvm_va_space_t *va_space = uvm_va_space_get(filp);
842*91676d66SBernhard Stoeckner 
843*91676d66SBernhard Stoeckner     uvm_va_space_down_write(va_space);
844*91676d66SBernhard Stoeckner     va_space->test.skip_migrate_vma = params->skip;
845*91676d66SBernhard Stoeckner     uvm_va_space_up_write(va_space);
846*91676d66SBernhard Stoeckner 
847*91676d66SBernhard Stoeckner     return NV_OK;
848*91676d66SBernhard Stoeckner }
849*91676d66SBernhard Stoeckner 
migrate_pageable_vma(struct vm_area_struct * vma,unsigned long start,unsigned long outer,migrate_vma_state_t * state,unsigned long * next_addr)8501739a20eSAndy Ritger static NV_STATUS migrate_pageable_vma(struct vm_area_struct *vma,
8511739a20eSAndy Ritger                                       unsigned long start,
8521739a20eSAndy Ritger                                       unsigned long outer,
8531739a20eSAndy Ritger                                       migrate_vma_state_t *state,
8541739a20eSAndy Ritger                                       unsigned long *next_addr)
8551739a20eSAndy Ritger {
8561739a20eSAndy Ritger     NV_STATUS status = NV_OK;
8571739a20eSAndy Ritger     struct mm_struct *mm = vma->vm_mm;
8581739a20eSAndy Ritger     uvm_migrate_args_t *uvm_migrate_args = state->uvm_migrate_args;
8591739a20eSAndy Ritger     uvm_va_space_t *va_space = uvm_migrate_args->va_space;
8601739a20eSAndy Ritger 
8611739a20eSAndy Ritger     UVM_ASSERT(PAGE_ALIGNED(start));
8621739a20eSAndy Ritger     UVM_ASSERT(PAGE_ALIGNED(outer));
8631739a20eSAndy Ritger     UVM_ASSERT(vma->vm_end > start);
8641739a20eSAndy Ritger     UVM_ASSERT(vma->vm_start < outer);
8651739a20eSAndy Ritger     uvm_assert_mmap_lock_locked(mm);
8661739a20eSAndy Ritger     uvm_assert_rwsem_locked(&va_space->lock);
8671739a20eSAndy Ritger 
8681739a20eSAndy Ritger     // Adjust to input range boundaries
8691739a20eSAndy Ritger     start = max(start, vma->vm_start);
8701739a20eSAndy Ritger     outer = min(outer, vma->vm_end);
8711739a20eSAndy Ritger 
872*91676d66SBernhard Stoeckner     if (va_space->test.skip_migrate_vma)
873*91676d66SBernhard Stoeckner         return NV_WARN_NOTHING_TO_DO;
874*91676d66SBernhard Stoeckner 
875b5bf85a8SAndy Ritger     // TODO: Bug 2419180: support file-backed pages in migrate_vma, when
876b5bf85a8SAndy Ritger     //       support for it is added to the Linux kernel
877b5bf85a8SAndy Ritger     if (!vma_is_anonymous(vma))
8781739a20eSAndy Ritger         return NV_WARN_NOTHING_TO_DO;
8791739a20eSAndy Ritger 
8801739a20eSAndy Ritger     if (uvm_processor_mask_empty(&va_space->registered_gpus))
8811739a20eSAndy Ritger         return NV_WARN_NOTHING_TO_DO;
8821739a20eSAndy Ritger 
8831739a20eSAndy Ritger     while (start < outer) {
8841739a20eSAndy Ritger         const size_t region_size = min(outer - start, UVM_MIGRATE_VMA_MAX_SIZE);
8851739a20eSAndy Ritger 
8861739a20eSAndy Ritger         status = migrate_pageable_vma_region(vma, start, start + region_size, state, next_addr);
8871739a20eSAndy Ritger         if (status == NV_ERR_MORE_PROCESSING_REQUIRED) {
8881739a20eSAndy Ritger             UVM_ASSERT(*next_addr >= start);
8891739a20eSAndy Ritger             UVM_ASSERT(*next_addr < outer);
8901739a20eSAndy Ritger         }
8911739a20eSAndy Ritger 
8921739a20eSAndy Ritger         if (status != NV_OK)
8931739a20eSAndy Ritger             break;
8941739a20eSAndy Ritger 
8951739a20eSAndy Ritger         start += region_size;
8961739a20eSAndy Ritger     };
8971739a20eSAndy Ritger 
8981739a20eSAndy Ritger     return status;
8991739a20eSAndy Ritger }
9001739a20eSAndy Ritger 
migrate_pageable(migrate_vma_state_t * state)9011739a20eSAndy Ritger static NV_STATUS migrate_pageable(migrate_vma_state_t *state)
9021739a20eSAndy Ritger {
9031739a20eSAndy Ritger     uvm_migrate_args_t *uvm_migrate_args = state->uvm_migrate_args;
9041739a20eSAndy Ritger     uvm_va_space_t *va_space = uvm_migrate_args->va_space;
9051739a20eSAndy Ritger     const unsigned long length = uvm_migrate_args->length;
9061739a20eSAndy Ritger     NvU64 *user_space_start = uvm_migrate_args->user_space_start;
9071739a20eSAndy Ritger     NvU64 *user_space_length = uvm_migrate_args->user_space_length;
9081739a20eSAndy Ritger     struct mm_struct *mm = uvm_migrate_args->mm;
9091739a20eSAndy Ritger     unsigned long start = uvm_migrate_args->start;
9101739a20eSAndy Ritger     unsigned long outer = start + length;
9111739a20eSAndy Ritger     unsigned long prev_outer = outer;
9121739a20eSAndy Ritger     struct vm_area_struct *vma;
9131739a20eSAndy Ritger 
9141739a20eSAndy Ritger     UVM_ASSERT(PAGE_ALIGNED(start));
9151739a20eSAndy Ritger     UVM_ASSERT(PAGE_ALIGNED(length));
9161739a20eSAndy Ritger     uvm_assert_mmap_lock_locked(mm);
9171739a20eSAndy Ritger 
9181739a20eSAndy Ritger     vma = find_vma_intersection(mm, start, outer);
9191739a20eSAndy Ritger     if (!vma || (start < vma->vm_start))
9201739a20eSAndy Ritger         return NV_ERR_INVALID_ADDRESS;
9211739a20eSAndy Ritger 
9221739a20eSAndy Ritger     // VMAs are validated and migrated one at a time, since migrate_vma works
9231739a20eSAndy Ritger     // on one vma at a time
92494eaea97SAndy Ritger     for (; vma->vm_start <= prev_outer; vma = find_vma_intersection(mm, prev_outer, outer)) {
9251739a20eSAndy Ritger         unsigned long next_addr = 0;
92694eaea97SAndy Ritger         NV_STATUS status;
92794eaea97SAndy Ritger 
92894eaea97SAndy Ritger         // Callers have already validated the range so the vma should be valid.
92994eaea97SAndy Ritger         UVM_ASSERT(vma);
93094eaea97SAndy Ritger 
93194eaea97SAndy Ritger         status = migrate_pageable_vma(vma, start, outer, state, &next_addr);
9321739a20eSAndy Ritger         if (status == NV_WARN_NOTHING_TO_DO) {
9331739a20eSAndy Ritger             NV_STATUS populate_status = NV_OK;
9341739a20eSAndy Ritger             bool touch = uvm_migrate_args->touch;
9351739a20eSAndy Ritger             uvm_populate_permissions_t populate_permissions = uvm_migrate_args->populate_permissions;
9361739a20eSAndy Ritger 
937*91676d66SBernhard Stoeckner             UVM_ASSERT(va_space->test.skip_migrate_vma ||
938*91676d66SBernhard Stoeckner                        !vma_is_anonymous(vma) ||
939*91676d66SBernhard Stoeckner                        uvm_processor_mask_empty(&va_space->registered_gpus));
9401739a20eSAndy Ritger 
9411739a20eSAndy Ritger             // We can't use migrate_vma to move the pages as desired. Normally
9421739a20eSAndy Ritger             // this fallback path is supposed to populate the memory then inform
9431739a20eSAndy Ritger             // user mode that it should call move_pages, but that move_pages
9441739a20eSAndy Ritger             // call won't work as expected if the caller is in the wrong
9451739a20eSAndy Ritger             // process. Make that failure explicit so the caller is aware that
9461739a20eSAndy Ritger             // move_pages won't behave as expected.
9471739a20eSAndy Ritger             //
9481739a20eSAndy Ritger             // If the caller is a kernel thread, such as the GPU BH, continue
9491739a20eSAndy Ritger             // with population since there's no move_pages fallback.
9501739a20eSAndy Ritger             if (current->mm != mm && !(current->flags & PF_KTHREAD))
9511739a20eSAndy Ritger                 return NV_ERR_NOT_SUPPORTED;
9521739a20eSAndy Ritger 
9531739a20eSAndy Ritger             // Populate pages with uvm_populate_pageable
9541739a20eSAndy Ritger             populate_status = uvm_populate_pageable_vma(vma, start, length, 0, touch, populate_permissions);
9551739a20eSAndy Ritger             if (populate_status == NV_OK) {
9561739a20eSAndy Ritger                 *user_space_start = max(vma->vm_start, start);
9571739a20eSAndy Ritger                 *user_space_length = min(vma->vm_end, outer) - *user_space_start;
9581739a20eSAndy Ritger             }
9591739a20eSAndy Ritger             else {
9601739a20eSAndy Ritger                 status = populate_status;
9611739a20eSAndy Ritger             }
9621739a20eSAndy Ritger         }
9631739a20eSAndy Ritger         else if (status == NV_ERR_MORE_PROCESSING_REQUIRED) {
9641739a20eSAndy Ritger             UVM_ASSERT(next_addr >= start);
9651739a20eSAndy Ritger             UVM_ASSERT(next_addr < outer);
9661739a20eSAndy Ritger             UVM_ASSERT(UVM_ID_IS_CPU(uvm_migrate_args->dst_id));
9671739a20eSAndy Ritger 
9681739a20eSAndy Ritger             *user_space_start = next_addr;
9691739a20eSAndy Ritger         }
9701739a20eSAndy Ritger 
9711739a20eSAndy Ritger         if (status != NV_OK)
9721739a20eSAndy Ritger             return status;
9731739a20eSAndy Ritger 
9741739a20eSAndy Ritger         if (vma->vm_end >= outer)
9751739a20eSAndy Ritger             return NV_OK;
9761739a20eSAndy Ritger 
9771739a20eSAndy Ritger         prev_outer = vma->vm_end;
9781739a20eSAndy Ritger     }
9791739a20eSAndy Ritger 
9801739a20eSAndy Ritger     // Input range not fully covered by VMAs.
9811739a20eSAndy Ritger     return NV_ERR_INVALID_ADDRESS;
9821739a20eSAndy Ritger }
9831739a20eSAndy Ritger 
uvm_migrate_pageable(uvm_migrate_args_t * uvm_migrate_args)9841739a20eSAndy Ritger NV_STATUS uvm_migrate_pageable(uvm_migrate_args_t *uvm_migrate_args)
9851739a20eSAndy Ritger {
9861739a20eSAndy Ritger     migrate_vma_state_t *state = NULL;
9871739a20eSAndy Ritger     NV_STATUS status;
9881739a20eSAndy Ritger     uvm_va_space_t *va_space = uvm_migrate_args->va_space;
9891739a20eSAndy Ritger     uvm_processor_id_t dst_id = uvm_migrate_args->dst_id;
9901739a20eSAndy Ritger 
9911739a20eSAndy Ritger     UVM_ASSERT(PAGE_ALIGNED(uvm_migrate_args->start));
9921739a20eSAndy Ritger     UVM_ASSERT(PAGE_ALIGNED(uvm_migrate_args->length));
9931739a20eSAndy Ritger     uvm_assert_mmap_lock_locked(uvm_migrate_args->mm);
9941739a20eSAndy Ritger 
9951739a20eSAndy Ritger     if (UVM_ID_IS_CPU(dst_id)) {
996*91676d66SBernhard Stoeckner         if (uvm_migrate_args->dst_node_id == -1)
9971739a20eSAndy Ritger             return NV_ERR_INVALID_ARGUMENT;
9981739a20eSAndy Ritger     }
9991739a20eSAndy Ritger     else {
10001739a20eSAndy Ritger         // Incoming dst_node_id is only valid if dst_id belongs to the CPU. Use
10011739a20eSAndy Ritger         // dst_node_id as the GPU node id if dst_id doesn't belong to the CPU.
1002eb5c7665SAndy Ritger         uvm_migrate_args->dst_node_id = uvm_gpu_numa_node(uvm_va_space_get_gpu(va_space, dst_id));
10031739a20eSAndy Ritger     }
10041739a20eSAndy Ritger 
10051739a20eSAndy Ritger     state = kmem_cache_alloc(g_uvm_migrate_vma_state_cache, NV_UVM_GFP_FLAGS);
10061739a20eSAndy Ritger     if (!state)
10071739a20eSAndy Ritger         return NV_ERR_NO_MEMORY;
10081739a20eSAndy Ritger 
10091739a20eSAndy Ritger     state->uvm_migrate_args = uvm_migrate_args;
10101739a20eSAndy Ritger     status = migrate_pageable(state);
10111739a20eSAndy Ritger 
10121739a20eSAndy Ritger     kmem_cache_free(g_uvm_migrate_vma_state_cache, state);
10131739a20eSAndy Ritger 
10141739a20eSAndy Ritger     return status;
10151739a20eSAndy Ritger }
10161739a20eSAndy Ritger 
uvm_migrate_pageable_init(void)1017811073c5SAndy Ritger NV_STATUS uvm_migrate_pageable_init(void)
10181739a20eSAndy Ritger {
10191739a20eSAndy Ritger     g_uvm_migrate_vma_state_cache = NV_KMEM_CACHE_CREATE("migrate_vma_state_t", migrate_vma_state_t);
10201739a20eSAndy Ritger     if (!g_uvm_migrate_vma_state_cache)
10211739a20eSAndy Ritger         return NV_ERR_NO_MEMORY;
10221739a20eSAndy Ritger 
10231739a20eSAndy Ritger     return NV_OK;
10241739a20eSAndy Ritger }
10251739a20eSAndy Ritger 
uvm_migrate_pageable_exit(void)1026811073c5SAndy Ritger void uvm_migrate_pageable_exit(void)
10271739a20eSAndy Ritger {
10281739a20eSAndy Ritger     kmem_cache_destroy_safe(&g_uvm_migrate_vma_state_cache);
10291739a20eSAndy Ritger }
10301739a20eSAndy Ritger #endif
1031