Commit 5ccbb057 authored by Rajneesh Bhardwaj's avatar Rajneesh Bhardwaj Committed by Alex Deucher

drm/amdkfd: CRIU Implement KFD checkpoint ioctl

This adds support to discover the  buffer objects that belong to a
process being checkpointed. The data corresponding to these buffer
objects is returned to user space plugin running under criu master
context which then stores this info to recreate these buffer objects
during a restore operation.
Reviewed-by: default avatarFelix Kuehling <Felix.Kuehling@amd.com>
Signed-off-by: default avatarDavid Yat Sin <david.yatsin@amd.com>
Signed-off-by: default avatarRajneesh Bhardwaj <rajneesh.bhardwaj@amd.com>
Signed-off-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent f185381b
...@@ -297,6 +297,7 @@ int amdgpu_amdkfd_get_tile_config(struct amdgpu_device *adev, ...@@ -297,6 +297,7 @@ int amdgpu_amdkfd_get_tile_config(struct amdgpu_device *adev,
struct tile_config *config); struct tile_config *config);
void amdgpu_amdkfd_ras_poison_consumption_handler(struct amdgpu_device *adev, void amdgpu_amdkfd_ras_poison_consumption_handler(struct amdgpu_device *adev,
bool reset); bool reset);
bool amdgpu_amdkfd_bo_mapped_to_dev(struct amdgpu_device *adev, struct kgd_mem *mem);
#if IS_ENABLED(CONFIG_HSA_AMD) #if IS_ENABLED(CONFIG_HSA_AMD)
void amdgpu_amdkfd_gpuvm_init_mem_limits(void); void amdgpu_amdkfd_gpuvm_init_mem_limits(void);
void amdgpu_amdkfd_gpuvm_destroy_cb(struct amdgpu_device *adev, void amdgpu_amdkfd_gpuvm_destroy_cb(struct amdgpu_device *adev,
......
...@@ -2629,3 +2629,14 @@ int amdgpu_amdkfd_get_tile_config(struct amdgpu_device *adev, ...@@ -2629,3 +2629,14 @@ int amdgpu_amdkfd_get_tile_config(struct amdgpu_device *adev,
return 0; return 0;
} }
bool amdgpu_amdkfd_bo_mapped_to_dev(struct amdgpu_device *adev, struct kgd_mem *mem)
{
struct kfd_mem_attachment *entry;
list_for_each_entry(entry, &mem->attachments, list) {
if (entry->is_mapped && entry->adev == adev)
return true;
}
return false;
}
...@@ -1136,6 +1136,26 @@ static void amdgpu_ttm_tt_unpopulate(struct ttm_device *bdev, ...@@ -1136,6 +1136,26 @@ static void amdgpu_ttm_tt_unpopulate(struct ttm_device *bdev,
return ttm_pool_free(&adev->mman.bdev.pool, ttm); return ttm_pool_free(&adev->mman.bdev.pool, ttm);
} }
/**
* amdgpu_ttm_tt_get_userptr - Return the userptr GTT ttm_tt for the current
* task
*
* @tbo: The ttm_buffer_object that contains the userptr
* @user_addr: The returned value
*/
int amdgpu_ttm_tt_get_userptr(const struct ttm_buffer_object *tbo,
uint64_t *user_addr)
{
struct amdgpu_ttm_tt *gtt;
if (!tbo->ttm)
return -EINVAL;
gtt = (void *)tbo->ttm;
*user_addr = gtt->userptr;
return 0;
}
/** /**
* amdgpu_ttm_tt_set_userptr - Initialize userptr GTT ttm_tt for the current * amdgpu_ttm_tt_set_userptr - Initialize userptr GTT ttm_tt for the current
* task * task
......
...@@ -181,6 +181,8 @@ static inline bool amdgpu_ttm_tt_get_user_pages_done(struct ttm_tt *ttm) ...@@ -181,6 +181,8 @@ static inline bool amdgpu_ttm_tt_get_user_pages_done(struct ttm_tt *ttm)
#endif #endif
void amdgpu_ttm_tt_set_user_pages(struct ttm_tt *ttm, struct page **pages); void amdgpu_ttm_tt_set_user_pages(struct ttm_tt *ttm, struct page **pages);
int amdgpu_ttm_tt_get_userptr(const struct ttm_buffer_object *tbo,
uint64_t *user_addr);
int amdgpu_ttm_tt_set_userptr(struct ttm_buffer_object *bo, int amdgpu_ttm_tt_set_userptr(struct ttm_buffer_object *bo,
uint64_t addr, uint32_t flags); uint64_t addr, uint32_t flags);
bool amdgpu_ttm_tt_has_userptr(struct ttm_tt *ttm); bool amdgpu_ttm_tt_has_userptr(struct ttm_tt *ttm);
......
...@@ -1860,6 +1860,29 @@ static int kfd_ioctl_svm(struct file *filep, struct kfd_process *p, void *data) ...@@ -1860,6 +1860,29 @@ static int kfd_ioctl_svm(struct file *filep, struct kfd_process *p, void *data)
} }
#endif #endif
static int criu_checkpoint_process(struct kfd_process *p,
uint8_t __user *user_priv_data,
uint64_t *priv_offset)
{
struct kfd_criu_process_priv_data process_priv;
int ret;
memset(&process_priv, 0, sizeof(process_priv));
process_priv.version = KFD_CRIU_PRIV_VERSION;
ret = copy_to_user(user_priv_data + *priv_offset,
&process_priv, sizeof(process_priv));
if (ret) {
pr_err("Failed to copy process information to user\n");
ret = -EFAULT;
}
*priv_offset += sizeof(process_priv);
return ret;
}
uint32_t get_process_num_bos(struct kfd_process *p) uint32_t get_process_num_bos(struct kfd_process *p)
{ {
uint32_t num_of_bos = 0; uint32_t num_of_bos = 0;
...@@ -1881,6 +1904,117 @@ uint32_t get_process_num_bos(struct kfd_process *p) ...@@ -1881,6 +1904,117 @@ uint32_t get_process_num_bos(struct kfd_process *p)
return num_of_bos; return num_of_bos;
} }
static int criu_checkpoint_bos(struct kfd_process *p,
uint32_t num_bos,
uint8_t __user *user_bos,
uint8_t __user *user_priv_data,
uint64_t *priv_offset)
{
struct kfd_criu_bo_bucket *bo_buckets;
struct kfd_criu_bo_priv_data *bo_privs;
int ret = 0, pdd_index, bo_index = 0, id;
void *mem;
bo_buckets = kvzalloc(num_bos * sizeof(*bo_buckets), GFP_KERNEL);
if (!bo_buckets) {
ret = -ENOMEM;
goto exit;
}
bo_privs = kvzalloc(num_bos * sizeof(*bo_privs), GFP_KERNEL);
if (!bo_privs) {
ret = -ENOMEM;
goto exit;
}
for (pdd_index = 0; pdd_index < p->n_pdds; pdd_index++) {
struct kfd_process_device *pdd = p->pdds[pdd_index];
struct amdgpu_bo *dumper_bo;
struct kgd_mem *kgd_mem;
idr_for_each_entry(&pdd->alloc_idr, mem, id) {
struct kfd_criu_bo_bucket *bo_bucket;
struct kfd_criu_bo_priv_data *bo_priv;
int i, dev_idx = 0;
if (!mem) {
ret = -ENOMEM;
goto exit;
}
kgd_mem = (struct kgd_mem *)mem;
dumper_bo = kgd_mem->bo;
if ((uint64_t)kgd_mem->va <= pdd->gpuvm_base)
continue;
bo_bucket = &bo_buckets[bo_index];
bo_priv = &bo_privs[bo_index];
bo_bucket->addr = (uint64_t)kgd_mem->va;
bo_bucket->size = amdgpu_bo_size(dumper_bo);
bo_bucket->gpu_id = pdd->dev->id;
bo_bucket->alloc_flags = (uint32_t)kgd_mem->alloc_flags;
bo_priv->idr_handle = id;
if (bo_bucket->alloc_flags & KFD_IOC_ALLOC_MEM_FLAGS_USERPTR) {
ret = amdgpu_ttm_tt_get_userptr(&dumper_bo->tbo,
&bo_priv->user_addr);
if (ret) {
pr_err("Failed to obtain user address for user-pointer bo\n");
goto exit;
}
}
if (bo_bucket->alloc_flags & KFD_IOC_ALLOC_MEM_FLAGS_DOORBELL)
bo_bucket->offset = KFD_MMAP_TYPE_DOORBELL |
KFD_MMAP_GPU_ID(pdd->dev->id);
else if (bo_bucket->alloc_flags &
KFD_IOC_ALLOC_MEM_FLAGS_MMIO_REMAP)
bo_bucket->offset = KFD_MMAP_TYPE_MMIO |
KFD_MMAP_GPU_ID(pdd->dev->id);
else
bo_bucket->offset = amdgpu_bo_mmap_offset(dumper_bo);
for (i = 0; i < p->n_pdds; i++) {
if (amdgpu_amdkfd_bo_mapped_to_dev(p->pdds[i]->dev->adev, kgd_mem))
bo_priv->mapped_gpuids[dev_idx++] = p->pdds[i]->dev->id;
}
pr_debug("bo_size = 0x%llx, bo_addr = 0x%llx bo_offset = 0x%llx\n"
"gpu_id = 0x%x alloc_flags = 0x%x idr_handle = 0x%x",
bo_bucket->size,
bo_bucket->addr,
bo_bucket->offset,
bo_bucket->gpu_id,
bo_bucket->alloc_flags,
bo_priv->idr_handle);
bo_index++;
}
}
ret = copy_to_user(user_bos, bo_buckets, num_bos * sizeof(*bo_buckets));
if (ret) {
pr_err("Failed to copy BO information to user\n");
ret = -EFAULT;
goto exit;
}
ret = copy_to_user(user_priv_data + *priv_offset, bo_privs, num_bos * sizeof(*bo_privs));
if (ret) {
pr_err("Failed to copy BO priv information to user\n");
ret = -EFAULT;
goto exit;
}
*priv_offset += num_bos * sizeof(*bo_privs);
exit:
kvfree(bo_buckets);
kvfree(bo_privs);
return ret;
}
static void criu_get_process_object_info(struct kfd_process *p, static void criu_get_process_object_info(struct kfd_process *p,
uint32_t *num_bos, uint32_t *num_bos,
uint64_t *objs_priv_size) uint64_t *objs_priv_size)
...@@ -1900,7 +2034,48 @@ static int criu_checkpoint(struct file *filep, ...@@ -1900,7 +2034,48 @@ static int criu_checkpoint(struct file *filep,
struct kfd_process *p, struct kfd_process *p,
struct kfd_ioctl_criu_args *args) struct kfd_ioctl_criu_args *args)
{ {
return 0; int ret;
uint32_t num_bos;
uint64_t priv_size, priv_offset = 0;
if (!args->bos || !args->priv_data)
return -EINVAL;
mutex_lock(&p->mutex);
if (!p->n_pdds) {
pr_err("No pdd for given process\n");
ret = -ENODEV;
goto exit_unlock;
}
criu_get_process_object_info(p, &num_bos, &priv_size);
if (num_bos != args->num_bos ||
priv_size != args->priv_data_size) {
ret = -EINVAL;
goto exit_unlock;
}
/* each function will store private data inside priv_data and adjust priv_offset */
ret = criu_checkpoint_process(p, (uint8_t __user *)args->priv_data, &priv_offset);
if (ret)
goto exit_unlock;
ret = criu_checkpoint_bos(p, num_bos, (uint8_t __user *)args->bos,
(uint8_t __user *)args->priv_data, &priv_offset);
if (ret)
goto exit_unlock;
exit_unlock:
mutex_unlock(&p->mutex);
if (ret)
pr_err("Failed to dump CRIU ret:%d\n", ret);
else
pr_debug("CRIU dump ret:%d\n", ret);
return ret;
} }
static int criu_restore(struct file *filep, static int criu_restore(struct file *filep,
......
...@@ -1049,7 +1049,9 @@ struct kfd_criu_device_priv_data { ...@@ -1049,7 +1049,9 @@ struct kfd_criu_device_priv_data {
}; };
struct kfd_criu_bo_priv_data { struct kfd_criu_bo_priv_data {
uint64_t reserved; uint64_t user_addr;
uint32_t idr_handle;
uint32_t mapped_gpuids[MAX_GPU_INSTANCE];
}; };
struct kfd_criu_svm_range_priv_data { struct kfd_criu_svm_range_priv_data {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment