if (gpuidx < MAX_GPU_INSTANCE) {
                bitmap_zero(ctx.bitmap, MAX_GPU_INSTANCE);
                bitmap_set(ctx.bitmap, gpuidx, 1);
+       } else if (ctx.process->xnack_enabled) {
+               bitmap_copy(ctx.bitmap, prange->bitmap_aip, MAX_GPU_INSTANCE);
+
+               /* If prefetch range to GPU, or GPU retry fault migrate range to
+                * GPU, which has ACCESS attribute to the range, create mapping
+                * on that GPU.
+                */
+               if (prange->actual_loc) {
+                       gpuidx = kfd_process_gpuidx_from_gpuid(ctx.process,
+                                                       prange->actual_loc);
+                       if (gpuidx < 0) {
+                               WARN_ONCE(1, "failed get device by id 0x%x\n",
+                                        prange->actual_loc);
+                               return -EINVAL;
+                       }
+                       if (test_bit(gpuidx, prange->bitmap_access))
+                               bitmap_set(ctx.bitmap, gpuidx, 1);
+               }
        } else {
                bitmap_or(ctx.bitmap, prange->bitmap_access,
                          prange->bitmap_aip, MAX_GPU_INSTANCE);
        return NULL;
 }
 
+/* svm_range_best_restore_location - decide the best fault restore location
+ * @prange: svm range structure
+ * @adev: the GPU on which vm fault happened
+ *
+ * This is only called when xnack is on, to decide the best location to restore
+ * the range mapping after GPU vm fault. Caller uses the best location to do
+ * migration if actual loc is not best location, then update GPU page table
+ * mapping to the best location.
+ *
+ * If vm fault gpu is range preferred loc, the best_loc is preferred loc.
+ * If vm fault gpu idx is on range ACCESSIBLE bitmap, best_loc is vm fault gpu
+ * If vm fault gpu idx is on range ACCESSIBLE_IN_PLACE bitmap, then
+ *    if range actual loc is cpu, best_loc is cpu
+ *    if vm fault gpu is on xgmi same hive of range actual loc gpu, best_loc is
+ *    range actual loc.
+ * Otherwise, GPU no access, best_loc is -1.
+ *
+ * Return:
+ * -1 means vm fault GPU no access
+ * 0 for CPU or GPU id
+ */
+static int32_t
+svm_range_best_restore_location(struct svm_range *prange,
+                               struct amdgpu_device *adev,
+                               int32_t *gpuidx)
+{
+       struct amdgpu_device *bo_adev;
+       struct kfd_process *p;
+       uint32_t gpuid;
+       int r;
+
+       p = container_of(prange->svms, struct kfd_process, svms);
+
+       r = kfd_process_gpuid_from_kgd(p, adev, &gpuid, gpuidx);
+       if (r < 0) {
+               pr_debug("failed to get gpuid from kgd\n");
+               return -1;
+       }
+
+       if (prange->preferred_loc == gpuid)
+               return prange->preferred_loc;
+
+       if (test_bit(*gpuidx, prange->bitmap_access))
+               return gpuid;
+
+       if (test_bit(*gpuidx, prange->bitmap_aip)) {
+               if (!prange->actual_loc)
+                       return 0;
+
+               bo_adev = svm_range_get_adev_by_id(prange, prange->actual_loc);
+               if (amdgpu_xgmi_same_hive(adev, bo_adev))
+                       return prange->actual_loc;
+               else
+                       return 0;
+       }
+
+       return -1;
+}
+
 int
 svm_range_restore_pages(struct amdgpu_device *adev, unsigned int pasid,
                        uint64_t addr)
 {
-       int r = 0;
        struct mm_struct *mm = NULL;
-       struct svm_range *prange;
        struct svm_range_list *svms;
+       struct svm_range *prange;
        struct kfd_process *p;
+       int32_t best_loc, gpuidx;
+       int r = 0;
 
        p = kfd_lookup_process_by_pasid(pasid);
        if (!p) {
 
        mutex_lock(&prange->migrate_mutex);
 
-       r = svm_range_validate_and_map(mm, prange, MAX_GPU_INSTANCE, false, false);
-       if (r)
-               pr_debug("failed %d to map svms 0x%p [0x%lx 0x%lx] to gpu\n", r,
+       best_loc = svm_range_best_restore_location(prange, adev, &gpuidx);
+       if (best_loc == -1) {
+               pr_debug("svms %p failed get best restore loc [0x%lx 0x%lx]\n",
                         svms, prange->start, prange->last);
+               r = -EACCES;
+               goto out_unlock_range;
+       }
+
+       pr_debug("svms %p [0x%lx 0x%lx] best restore 0x%x, actual loc 0x%x\n",
+                svms, prange->start, prange->last, best_loc,
+                prange->actual_loc);
 
+       if (prange->actual_loc != best_loc) {
+               if (best_loc) {
+                       r = svm_migrate_ram_to_vram(prange, best_loc, mm);
+                       if (r) {
+                               pr_debug("svm_migrate_to_vram failed (%d) at %llx, falling back to system memory\n",
+                                        r, addr);
+                               /* Fallback to system memory if migration to
+                                * VRAM failed
+                                */
+                               if (prange->actual_loc)
+                                       r = svm_migrate_vram_to_ram(prange, mm);
+                               else
+                                       r = 0;
+                       }
+               } else {
+                       r = svm_migrate_vram_to_ram(prange, mm);
+               }
+               if (r) {
+                       pr_debug("failed %d to migrate svms %p [0x%lx 0x%lx]\n",
+                                r, svms, prange->start, prange->last);
+                       goto out_unlock_range;
+               }
+       }
+
+       r = svm_range_validate_and_map(mm, prange, gpuidx, false, false);
+       if (r)
+               pr_debug("failed %d to map svms 0x%p [0x%lx 0x%lx] to gpus\n",
+                        r, svms, prange->start, prange->last);
+
+out_unlock_range:
        mutex_unlock(&prange->migrate_mutex);
 out_unlock_svms:
        mutex_unlock(&svms->lock);
        return 0;
 }
 
-/* svm_range_best_location - decide the best actual location
+/* svm_range_best_prefetch_location - decide the best prefetch location
  * @prange: svm range structure
  *
  * For xnack off:
  * Return:
  * 0 for CPU or GPU id
  */
-static uint32_t svm_range_best_location(struct svm_range *prange)
+static uint32_t
+svm_range_best_prefetch_location(struct svm_range *prange)
 {
        DECLARE_BITMAP(bitmap, MAX_GPU_INSTANCE);
        uint32_t best_loc = prange->prefetch_loc;
        int r = 0;
 
        *migrated = false;
-       best_loc = svm_range_best_location(prange);
+       best_loc = svm_range_best_prefetch_location(prange);
 
        if (best_loc == KFD_IOCTL_SVM_LOCATION_UNDEFINED ||
            best_loc == prange->actual_loc)
 
        if (best_loc) {
                pr_debug("migrate from ram to vram\n");
-               r = svm_migrate_ram_to_vram(prange, best_loc);
+               r = svm_migrate_ram_to_vram(prange, best_loc, mm);
        } else {
                pr_debug("migrate from vram to ram\n");
-               r = svm_migrate_vram_to_ram(prange, current->mm);
+               r = svm_migrate_vram_to_ram(prange, mm);
        }
 
        if (!r)