| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467 |
- // SPDX-License-Identifier: GPL-2.0 OR MIT
- /*
- * Copyright 2014-2022 Advanced Micro Devices, Inc.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
- * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- *
- */
- #include <linux/slab.h>
- #include "kfd_priv.h"
- #include "kfd_topology.h"
- #include "kfd_svm.h"
- void print_queue_properties(struct queue_properties *q)
- {
- if (!q)
- return;
- pr_debug("Printing queue properties:\n");
- pr_debug("Queue Type: %u\n", q->type);
- pr_debug("Queue Size: %llu\n", q->queue_size);
- pr_debug("Queue percent: %u\n", q->queue_percent);
- pr_debug("Queue Address: 0x%llX\n", q->queue_address);
- pr_debug("Queue Id: %u\n", q->queue_id);
- pr_debug("Queue Process Vmid: %u\n", q->vmid);
- pr_debug("Queue Read Pointer: 0x%px\n", q->read_ptr);
- pr_debug("Queue Write Pointer: 0x%px\n", q->write_ptr);
- pr_debug("Queue Doorbell Pointer: 0x%p\n", q->doorbell_ptr);
- pr_debug("Queue Doorbell Offset: %u\n", q->doorbell_off);
- }
- void print_queue(struct queue *q)
- {
- if (!q)
- return;
- pr_debug("Printing queue:\n");
- pr_debug("Queue Type: %u\n", q->properties.type);
- pr_debug("Queue Size: %llu\n", q->properties.queue_size);
- pr_debug("Queue percent: %u\n", q->properties.queue_percent);
- pr_debug("Queue Address: 0x%llX\n", q->properties.queue_address);
- pr_debug("Queue Id: %u\n", q->properties.queue_id);
- pr_debug("Queue Process Vmid: %u\n", q->properties.vmid);
- pr_debug("Queue Read Pointer: 0x%px\n", q->properties.read_ptr);
- pr_debug("Queue Write Pointer: 0x%px\n", q->properties.write_ptr);
- pr_debug("Queue Doorbell Pointer: 0x%p\n", q->properties.doorbell_ptr);
- pr_debug("Queue Doorbell Offset: %u\n", q->properties.doorbell_off);
- pr_debug("Queue MQD Address: 0x%p\n", q->mqd);
- pr_debug("Queue MQD Gart: 0x%llX\n", q->gart_mqd_addr);
- pr_debug("Queue Process Address: 0x%p\n", q->process);
- pr_debug("Queue Device Address: 0x%p\n", q->device);
- }
- int init_queue(struct queue **q, const struct queue_properties *properties)
- {
- struct queue *tmp_q;
- tmp_q = kzalloc(sizeof(*tmp_q), GFP_KERNEL);
- if (!tmp_q)
- return -ENOMEM;
- memcpy(&tmp_q->properties, properties, sizeof(*properties));
- *q = tmp_q;
- return 0;
- }
- void uninit_queue(struct queue *q)
- {
- kfree(q);
- }
- #if IS_ENABLED(CONFIG_HSA_AMD_SVM)
- static int kfd_queue_buffer_svm_get(struct kfd_process_device *pdd, u64 addr, u64 size)
- {
- struct kfd_process *p = pdd->process;
- struct list_head update_list;
- struct svm_range *prange;
- int ret = -EINVAL;
- INIT_LIST_HEAD(&update_list);
- addr >>= PAGE_SHIFT;
- size >>= PAGE_SHIFT;
- mutex_lock(&p->svms.lock);
- /*
- * range may split to multiple svm pranges aligned to granularity boundaery.
- */
- while (size) {
- uint32_t gpuid, gpuidx;
- int r;
- prange = svm_range_from_addr(&p->svms, addr, NULL);
- if (!prange)
- break;
- if (!prange->mapped_to_gpu)
- break;
- r = kfd_process_gpuid_from_node(p, pdd->dev, &gpuid, &gpuidx);
- if (r < 0)
- break;
- if (!test_bit(gpuidx, prange->bitmap_access) &&
- !test_bit(gpuidx, prange->bitmap_aip))
- break;
- if (!(prange->flags & KFD_IOCTL_SVM_FLAG_GPU_ALWAYS_MAPPED))
- break;
- list_add(&prange->update_list, &update_list);
- if (prange->last - prange->start + 1 >= size) {
- size = 0;
- break;
- }
- size -= prange->last - prange->start + 1;
- addr += prange->last - prange->start + 1;
- }
- if (size) {
- pr_debug("[0x%llx 0x%llx] not registered\n", addr, addr + size - 1);
- goto out_unlock;
- }
- list_for_each_entry(prange, &update_list, update_list)
- atomic_inc(&prange->queue_refcount);
- ret = 0;
- out_unlock:
- mutex_unlock(&p->svms.lock);
- return ret;
- }
- static void kfd_queue_buffer_svm_put(struct kfd_process_device *pdd, u64 addr, u64 size)
- {
- struct kfd_process *p = pdd->process;
- struct svm_range *prange, *pchild;
- struct interval_tree_node *node;
- unsigned long last;
- addr >>= PAGE_SHIFT;
- last = addr + (size >> PAGE_SHIFT) - 1;
- mutex_lock(&p->svms.lock);
- node = interval_tree_iter_first(&p->svms.objects, addr, last);
- while (node) {
- struct interval_tree_node *next_node;
- unsigned long next_start;
- prange = container_of(node, struct svm_range, it_node);
- next_node = interval_tree_iter_next(node, addr, last);
- next_start = min(node->last, last) + 1;
- if (atomic_add_unless(&prange->queue_refcount, -1, 0)) {
- list_for_each_entry(pchild, &prange->child_list, child_list)
- atomic_add_unless(&pchild->queue_refcount, -1, 0);
- }
- node = next_node;
- addr = next_start;
- }
- mutex_unlock(&p->svms.lock);
- }
- #else
- static int kfd_queue_buffer_svm_get(struct kfd_process_device *pdd, u64 addr, u64 size)
- {
- return -EINVAL;
- }
- static void kfd_queue_buffer_svm_put(struct kfd_process_device *pdd, u64 addr, u64 size)
- {
- }
- #endif
- int kfd_queue_buffer_get(struct amdgpu_vm *vm, void __user *addr, struct amdgpu_bo **pbo,
- u64 expected_size)
- {
- struct amdgpu_bo_va_mapping *mapping;
- u64 user_addr;
- u64 size;
- user_addr = (u64)addr >> AMDGPU_GPU_PAGE_SHIFT;
- size = expected_size >> AMDGPU_GPU_PAGE_SHIFT;
- mapping = amdgpu_vm_bo_lookup_mapping(vm, user_addr);
- if (!mapping)
- goto out_err;
- if (user_addr != mapping->start ||
- (size != 0 && user_addr + size - 1 != mapping->last)) {
- pr_debug("expected size 0x%llx not equal to mapping addr 0x%llx size 0x%llx\n",
- expected_size, mapping->start << AMDGPU_GPU_PAGE_SHIFT,
- (mapping->last - mapping->start + 1) << AMDGPU_GPU_PAGE_SHIFT);
- goto out_err;
- }
- *pbo = amdgpu_bo_ref(mapping->bo_va->base.bo);
- mapping->bo_va->queue_refcount++;
- return 0;
- out_err:
- *pbo = NULL;
- return -EINVAL;
- }
- /* FIXME: remove this function, just call amdgpu_bo_unref directly */
- void kfd_queue_buffer_put(struct amdgpu_bo **bo)
- {
- amdgpu_bo_unref(bo);
- }
- int kfd_queue_acquire_buffers(struct kfd_process_device *pdd, struct queue_properties *properties)
- {
- struct kfd_topology_device *topo_dev;
- u64 expected_queue_size;
- struct amdgpu_vm *vm;
- u32 total_cwsr_size;
- int err;
- topo_dev = kfd_topology_device_by_id(pdd->dev->id);
- if (!topo_dev)
- return -EINVAL;
- /* AQL queues on GFX7 and GFX8 appear twice their actual size */
- if (properties->type == KFD_QUEUE_TYPE_COMPUTE &&
- properties->format == KFD_QUEUE_FORMAT_AQL &&
- topo_dev->node_props.gfx_target_version >= 70000 &&
- topo_dev->node_props.gfx_target_version < 90000)
- expected_queue_size = properties->queue_size / 2;
- else
- expected_queue_size = properties->queue_size;
- vm = drm_priv_to_vm(pdd->drm_priv);
- err = amdgpu_bo_reserve(vm->root.bo, false);
- if (err)
- return err;
- err = kfd_queue_buffer_get(vm, properties->write_ptr, &properties->wptr_bo, PAGE_SIZE);
- if (err)
- goto out_err_unreserve;
- err = kfd_queue_buffer_get(vm, properties->read_ptr, &properties->rptr_bo, PAGE_SIZE);
- if (err)
- goto out_err_unreserve;
- err = kfd_queue_buffer_get(vm, (void *)properties->queue_address,
- &properties->ring_bo, expected_queue_size);
- if (err)
- goto out_err_unreserve;
- /* only compute queue requires EOP buffer and CWSR area */
- if (properties->type != KFD_QUEUE_TYPE_COMPUTE)
- goto out_unreserve;
- /* EOP buffer is not required for all ASICs */
- if (properties->eop_ring_buffer_address) {
- if (properties->eop_ring_buffer_size != topo_dev->node_props.eop_buffer_size) {
- pr_debug("queue eop bo size 0x%x not equal to node eop buf size 0x%x\n",
- properties->eop_ring_buffer_size,
- topo_dev->node_props.eop_buffer_size);
- err = -EINVAL;
- goto out_err_unreserve;
- }
- err = kfd_queue_buffer_get(vm, (void *)properties->eop_ring_buffer_address,
- &properties->eop_buf_bo,
- properties->eop_ring_buffer_size);
- if (err)
- goto out_err_unreserve;
- }
- if (properties->ctl_stack_size != topo_dev->node_props.ctl_stack_size) {
- pr_debug("queue ctl stack size 0x%x not equal to node ctl stack size 0x%x\n",
- properties->ctl_stack_size,
- topo_dev->node_props.ctl_stack_size);
- err = -EINVAL;
- goto out_err_unreserve;
- }
- if (properties->ctx_save_restore_area_size != topo_dev->node_props.cwsr_size) {
- pr_debug("queue cwsr size 0x%x not equal to node cwsr size 0x%x\n",
- properties->ctx_save_restore_area_size,
- topo_dev->node_props.cwsr_size);
- err = -EINVAL;
- goto out_err_unreserve;
- }
- total_cwsr_size = (topo_dev->node_props.cwsr_size + topo_dev->node_props.debug_memory_size)
- * NUM_XCC(pdd->dev->xcc_mask);
- total_cwsr_size = ALIGN(total_cwsr_size, PAGE_SIZE);
- err = kfd_queue_buffer_get(vm, (void *)properties->ctx_save_restore_area_address,
- &properties->cwsr_bo, total_cwsr_size);
- if (!err)
- goto out_unreserve;
- amdgpu_bo_unreserve(vm->root.bo);
- err = kfd_queue_buffer_svm_get(pdd, properties->ctx_save_restore_area_address,
- total_cwsr_size);
- if (err)
- goto out_err_release;
- return 0;
- out_unreserve:
- amdgpu_bo_unreserve(vm->root.bo);
- return 0;
- out_err_unreserve:
- amdgpu_bo_unreserve(vm->root.bo);
- out_err_release:
- /* FIXME: make a _locked version of this that can be called before
- * dropping the VM reservation.
- */
- kfd_queue_unref_bo_vas(pdd, properties);
- kfd_queue_release_buffers(pdd, properties);
- return err;
- }
- int kfd_queue_release_buffers(struct kfd_process_device *pdd, struct queue_properties *properties)
- {
- struct kfd_topology_device *topo_dev;
- u32 total_cwsr_size;
- kfd_queue_buffer_put(&properties->wptr_bo);
- kfd_queue_buffer_put(&properties->rptr_bo);
- kfd_queue_buffer_put(&properties->ring_bo);
- kfd_queue_buffer_put(&properties->eop_buf_bo);
- kfd_queue_buffer_put(&properties->cwsr_bo);
- topo_dev = kfd_topology_device_by_id(pdd->dev->id);
- if (!topo_dev)
- return -EINVAL;
- total_cwsr_size = (topo_dev->node_props.cwsr_size + topo_dev->node_props.debug_memory_size)
- * NUM_XCC(pdd->dev->xcc_mask);
- total_cwsr_size = ALIGN(total_cwsr_size, PAGE_SIZE);
- kfd_queue_buffer_svm_put(pdd, properties->ctx_save_restore_area_address, total_cwsr_size);
- return 0;
- }
- void kfd_queue_unref_bo_va(struct amdgpu_vm *vm, struct amdgpu_bo **bo)
- {
- if (*bo) {
- struct amdgpu_bo_va *bo_va;
- bo_va = amdgpu_vm_bo_find(vm, *bo);
- if (bo_va && bo_va->queue_refcount)
- bo_va->queue_refcount--;
- }
- }
- int kfd_queue_unref_bo_vas(struct kfd_process_device *pdd,
- struct queue_properties *properties)
- {
- struct amdgpu_vm *vm;
- int err;
- vm = drm_priv_to_vm(pdd->drm_priv);
- err = amdgpu_bo_reserve(vm->root.bo, false);
- if (err)
- return err;
- kfd_queue_unref_bo_va(vm, &properties->wptr_bo);
- kfd_queue_unref_bo_va(vm, &properties->rptr_bo);
- kfd_queue_unref_bo_va(vm, &properties->ring_bo);
- kfd_queue_unref_bo_va(vm, &properties->eop_buf_bo);
- kfd_queue_unref_bo_va(vm, &properties->cwsr_bo);
- amdgpu_bo_unreserve(vm->root.bo);
- return 0;
- }
- #define SGPR_SIZE_PER_CU 0x4000
- #define LDS_SIZE_PER_CU 0x10000
- #define HWREG_SIZE_PER_CU 0x1000
- #define DEBUGGER_BYTES_ALIGN 64
- #define DEBUGGER_BYTES_PER_WAVE 32
- static u32 kfd_get_vgpr_size_per_cu(u32 gfxv)
- {
- u32 vgpr_size = 0x40000;
- if ((gfxv / 100 * 100) == 90400 || /* GFX_VERSION_AQUA_VANJARAM */
- gfxv == 90010 || /* GFX_VERSION_ALDEBARAN */
- gfxv == 90008) /* GFX_VERSION_ARCTURUS */
- vgpr_size = 0x80000;
- else if (gfxv == 110000 || /* GFX_VERSION_PLUM_BONITO */
- gfxv == 110001 || /* GFX_VERSION_WHEAT_NAS */
- gfxv == 120000 || /* GFX_VERSION_GFX1200 */
- gfxv == 120001) /* GFX_VERSION_GFX1201 */
- vgpr_size = 0x60000;
- return vgpr_size;
- }
- #define WG_CONTEXT_DATA_SIZE_PER_CU(gfxv) \
- (kfd_get_vgpr_size_per_cu(gfxv) + SGPR_SIZE_PER_CU +\
- LDS_SIZE_PER_CU + HWREG_SIZE_PER_CU)
- #define CNTL_STACK_BYTES_PER_WAVE(gfxv) \
- ((gfxv) >= 100100 ? 12 : 8) /* GFX_VERSION_NAVI10*/
- #define SIZEOF_HSA_USER_CONTEXT_SAVE_AREA_HEADER 40
- void kfd_queue_ctx_save_restore_size(struct kfd_topology_device *dev)
- {
- struct kfd_node_properties *props = &dev->node_props;
- u32 gfxv = props->gfx_target_version;
- u32 ctl_stack_size;
- u32 wg_data_size;
- u32 wave_num;
- u32 cu_num;
- if (gfxv < 80001) /* GFX_VERSION_CARRIZO */
- return;
- cu_num = props->simd_count / props->simd_per_cu / NUM_XCC(dev->gpu->xcc_mask);
- wave_num = (gfxv < 100100) ? /* GFX_VERSION_NAVI10 */
- min(cu_num * 40, props->array_count / props->simd_arrays_per_engine * 512)
- : cu_num * 32;
- wg_data_size = ALIGN(cu_num * WG_CONTEXT_DATA_SIZE_PER_CU(gfxv), PAGE_SIZE);
- ctl_stack_size = wave_num * CNTL_STACK_BYTES_PER_WAVE(gfxv) + 8;
- ctl_stack_size = ALIGN(SIZEOF_HSA_USER_CONTEXT_SAVE_AREA_HEADER + ctl_stack_size,
- PAGE_SIZE);
- if ((gfxv / 10000 * 10000) == 100000) {
- /* HW design limits control stack size to 0x7000.
- * This is insufficient for theoretical PM4 cases
- * but sufficient for AQL, limited by SPI events.
- */
- ctl_stack_size = min(ctl_stack_size, 0x7000);
- }
- props->ctl_stack_size = ctl_stack_size;
- props->debug_memory_size = ALIGN(wave_num * DEBUGGER_BYTES_PER_WAVE, DEBUGGER_BYTES_ALIGN);
- props->cwsr_size = ctl_stack_size + wg_data_size;
- if (gfxv == 80002) /* GFX_VERSION_TONGA */
- props->eop_buffer_size = 0x8000;
- else if ((gfxv / 100 * 100) == 90400) /* GFX_VERSION_AQUA_VANJARAM */
- props->eop_buffer_size = 4096;
- else if (gfxv >= 80000)
- props->eop_buffer_size = 4096;
- }
|