/* * Copyright 2016-2022 HabanaLabs, Ltd. * All Rights Reserved.
*/
#include"habanalabs.h"
#include <linux/slab.h>
/** * struct hl_eqe_work - This structure is used to schedule work of EQ * entry and cpucp_reset event * * @eq_work: workqueue object to run when EQ entry is received * @hdev: pointer to device structure * @eq_entry: copy of the EQ entry
*/ struct hl_eqe_work { struct work_struct eq_work; struct hl_device *hdev; struct hl_eq_entry eq_entry;
};
/** * hl_cq_inc_ptr - increment ci or pi of cq * * @ptr: the current ci or pi value of the completion queue * * Increment ptr by 1. If it reaches the number of completion queue * entries, set it to 0
*/ inline u32 hl_cq_inc_ptr(u32 ptr)
{
ptr++; if (unlikely(ptr == HL_CQ_LENGTH))
ptr = 0; return ptr;
}
/** * hl_eq_inc_ptr - increment ci of eq * * @ptr: the current ci value of the event queue * * Increment ptr by 1. If it reaches the number of event queue * entries, set it to 0
*/ staticinline u32 hl_eq_inc_ptr(u32 ptr)
{
ptr++; if (unlikely(ptr == HL_EQ_LENGTH))
ptr = 0; return ptr;
}
cs = hdev->shadow_cs_queue[cs_seq & (prop->max_pending_cs - 1)]; if (!cs) {
dev_warn(hdev->dev, "No pointer to CS in shadow array at index %d\n",
cs_seq); return;
}
/* * CQ interrupt handler has 2 modes of operation: * 1. Interrupt per CS completion: (Single CQ for all queues) * CQ entry represents a completed CS * * 2. Interrupt per CS job completion in queue: (CQ per queue) * CQ entry represents a completed job in a certain queue
*/ if (shadow_index_valid && !hdev->disabled) { if (hdev->asic_prop.completion_mode ==
HL_COMPLETION_MODE_CS)
cs_finish(hdev, shadow_index, timestamp); else
job_finish(hdev, shadow_index, cq, timestamp);
}
if (dynamic_alloc_free_list_head) {
list_for_each_entry_safe(free_obj, temp_free_obj, dynamic_alloc_free_list_head,
free_objects_node) {
dev_dbg(hdev->dev, "Dynamic_Alloc list: About to put refcount to buf (%p) cq_cb(%p)\n",
free_obj->buf,
free_obj->cq_cb);
/* * This function called with spin_lock of wait_list_lock taken * This function will set timestamp and delete the registration node from the * wait_list_lock. * and since we're protected with spin_lock here, so we cannot just put the refcount * for the objects here, since the release function may be called and it's also a long * logic (which might sleep also) that cannot be handled in irq context. * so here we'll be filling a list with nodes of "put" jobs and then will send this * list to a dedicated workqueue to do the actual put.
*/ staticint handle_registration_node(struct hl_device *hdev, struct hl_user_pending_interrupt *pend, struct list_head **free_list, struct list_head **dynamic_alloc_list, struct hl_user_interrupt *intr)
{ struct hl_ts_free_jobs *ts_free_jobs_data; struct timestamp_reg_free_node *free_node;
u32 free_node_index;
u64 timestamp;
/* Putting the refcount for ts_buff and cq_cb objects will be handled * in workqueue context, just add job to free_list.
*/
free_node->buf = pend->ts_reg_info.buf;
free_node->cq_cb = pend->ts_reg_info.cq_cb;
/* For registration nodes: * As part of handling the registration nodes, we should put refcount to * some objects. the problem is that we cannot do that under spinlock * or in irq handler context at all (since release functions are long and * might sleep), so we will need to handle that part in workqueue context. * To avoid handling kmalloc failure which compels us rolling back actions * and move nodes hanged on the free list back to the interrupt ts list * we always alloc the job of the WQ at the beginning.
*/
job = kmalloc(sizeof(*job), GFP_ATOMIC); if (!job) return;
while (1) {
cur_eqe = le32_to_cpu(eq_base[eq->ci].hdr.ctl);
entry_ready = !!FIELD_GET(EQ_CTL_READY_MASK, cur_eqe);
if (!entry_ready) break;
cur_eqe_index = FIELD_GET(EQ_CTL_INDEX_MASK, cur_eqe); if ((hdev->event_queue.check_eqe_index) &&
(((eq->prev_eqe_index + 1) & EQ_CTL_INDEX_MASK) != cur_eqe_index)) {
dev_err(hdev->dev, "EQE %#x in queue is ready but index does not match %d!=%d",
cur_eqe,
((eq->prev_eqe_index + 1) & EQ_CTL_INDEX_MASK),
cur_eqe_index); break;
}
eq->prev_eqe_index++;
eq_entry = &eq_base[eq->ci];
/* * Make sure we read EQ entry contents after we've * checked the ownership bit.
*/
dma_rmb();
if (hdev->disabled && !hdev->reset_info.in_compute_reset) {
ctl = le32_to_cpu(eq_entry->hdr.ctl);
event_type = ((ctl & EQ_CTL_EVENT_TYPE_MASK) >> EQ_CTL_EVENT_TYPE_SHIFT);
dev_warn(hdev->dev, "Device disabled but received an EQ event (%u)\n", event_type); goto skip_irq;
}
/** * hl_cq_init - main initialization function for an cq object * * @hdev: pointer to device structure * @q: pointer to cq structure * @hw_queue_id: The H/W queue ID this completion queue belongs to * HL_INVALID_QUEUE if cq is not attached to any specific queue * * Allocate dma-able memory for the completion queue and initialize fields * Returns 0 on success
*/ int hl_cq_init(struct hl_device *hdev, struct hl_cq *q, u32 hw_queue_id)
{ void *p;
p = hl_asic_dma_alloc_coherent(hdev, HL_CQ_SIZE_IN_BYTES, &q->bus_address,
GFP_KERNEL | __GFP_ZERO); if (!p) return -ENOMEM;
/* * It's not enough to just reset the PI/CI because the H/W may have * written valid completion entries before it was halted and therefore * we need to clean the actual queues so we won't process old entries * when the device is operational again
*/
/* * It's not enough to just reset the PI/CI because the H/W may have * written valid completion entries before it was halted and therefore * we need to clean the actual queues so we won't process old entries * when the device is operational again
*/
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.