Path: blob/master/drivers/accel/habanalabs/common/irq.c
26436 views
// SPDX-License-Identifier: GPL-2.012/*3* Copyright 2016-2022 HabanaLabs, Ltd.4* All Rights Reserved.5*/67#include "habanalabs.h"89#include <linux/slab.h>1011/**12* struct hl_eqe_work - This structure is used to schedule work of EQ13* entry and cpucp_reset event14*15* @eq_work: workqueue object to run when EQ entry is received16* @hdev: pointer to device structure17* @eq_entry: copy of the EQ entry18*/19struct hl_eqe_work {20struct work_struct eq_work;21struct hl_device *hdev;22struct hl_eq_entry eq_entry;23};2425/**26* hl_cq_inc_ptr - increment ci or pi of cq27*28* @ptr: the current ci or pi value of the completion queue29*30* Increment ptr by 1. If it reaches the number of completion queue31* entries, set it to 032*/33inline u32 hl_cq_inc_ptr(u32 ptr)34{35ptr++;36if (unlikely(ptr == HL_CQ_LENGTH))37ptr = 0;38return ptr;39}4041/**42* hl_eq_inc_ptr - increment ci of eq43*44* @ptr: the current ci value of the event queue45*46* Increment ptr by 1. If it reaches the number of event queue47* entries, set it to 048*/49static inline u32 hl_eq_inc_ptr(u32 ptr)50{51ptr++;52if (unlikely(ptr == HL_EQ_LENGTH))53ptr = 0;54return ptr;55}5657static void irq_handle_eqe(struct work_struct *work)58{59struct hl_eqe_work *eqe_work = container_of(work, struct hl_eqe_work,60eq_work);61struct hl_device *hdev = eqe_work->hdev;6263hdev->asic_funcs->handle_eqe(hdev, &eqe_work->eq_entry);6465kfree(eqe_work);66}6768/**69* job_finish - queue job finish work70*71* @hdev: pointer to device structure72* @cs_seq: command submission sequence73* @cq: completion queue74* @timestamp: interrupt timestamp75*76*/77static void job_finish(struct hl_device *hdev, u32 cs_seq, struct hl_cq *cq, ktime_t timestamp)78{79struct hl_hw_queue *queue;80struct hl_cs_job *job;8182queue = &hdev->kernel_queues[cq->hw_queue_id];83job = queue->shadow_queue[hl_pi_2_offset(cs_seq)];84job->timestamp = timestamp;85queue_work(hdev->cq_wq[cq->cq_idx], &job->finish_work);8687atomic_inc(&queue->ci);88}8990/**91* cs_finish - queue all cs jobs finish work92*93* @hdev: pointer to device structure94* @cs_seq: command submission sequence95* @timestamp: interrupt timestamp96*97*/98static void cs_finish(struct hl_device *hdev, u16 cs_seq, ktime_t timestamp)99{100struct asic_fixed_properties *prop = &hdev->asic_prop;101struct hl_hw_queue *queue;102struct hl_cs *cs;103struct hl_cs_job *job;104105cs = hdev->shadow_cs_queue[cs_seq & (prop->max_pending_cs - 1)];106if (!cs) {107dev_warn(hdev->dev,108"No pointer to CS in shadow array at index %d\n",109cs_seq);110return;111}112113list_for_each_entry(job, &cs->job_list, cs_node) {114queue = &hdev->kernel_queues[job->hw_queue_id];115atomic_inc(&queue->ci);116}117118cs->completion_timestamp = timestamp;119queue_work(hdev->cs_cmplt_wq, &cs->finish_work);120}121122/**123* hl_irq_handler_cq - irq handler for completion queue124*125* @irq: irq number126* @arg: pointer to completion queue structure127*128*/129irqreturn_t hl_irq_handler_cq(int irq, void *arg)130{131struct hl_cq *cq = arg;132struct hl_device *hdev = cq->hdev;133bool shadow_index_valid, entry_ready;134u16 shadow_index;135struct hl_cq_entry *cq_entry, *cq_base;136ktime_t timestamp = ktime_get();137138if (hdev->disabled) {139dev_dbg(hdev->dev,140"Device disabled but received IRQ %d for CQ %d\n",141irq, cq->hw_queue_id);142return IRQ_HANDLED;143}144145cq_base = cq->kernel_address;146147while (1) {148cq_entry = (struct hl_cq_entry *) &cq_base[cq->ci];149150entry_ready = !!FIELD_GET(CQ_ENTRY_READY_MASK,151le32_to_cpu(cq_entry->data));152if (!entry_ready)153break;154155/* Make sure we read CQ entry contents after we've156* checked the ownership bit.157*/158dma_rmb();159160shadow_index_valid =161!!FIELD_GET(CQ_ENTRY_SHADOW_INDEX_VALID_MASK,162le32_to_cpu(cq_entry->data));163164shadow_index = FIELD_GET(CQ_ENTRY_SHADOW_INDEX_MASK,165le32_to_cpu(cq_entry->data));166167/*168* CQ interrupt handler has 2 modes of operation:169* 1. Interrupt per CS completion: (Single CQ for all queues)170* CQ entry represents a completed CS171*172* 2. Interrupt per CS job completion in queue: (CQ per queue)173* CQ entry represents a completed job in a certain queue174*/175if (shadow_index_valid && !hdev->disabled) {176if (hdev->asic_prop.completion_mode ==177HL_COMPLETION_MODE_CS)178cs_finish(hdev, shadow_index, timestamp);179else180job_finish(hdev, shadow_index, cq, timestamp);181}182183/* Clear CQ entry ready bit */184cq_entry->data = cpu_to_le32(le32_to_cpu(cq_entry->data) &185~CQ_ENTRY_READY_MASK);186187cq->ci = hl_cq_inc_ptr(cq->ci);188189/* Increment free slots */190atomic_inc(&cq->free_slots_cnt);191}192193return IRQ_HANDLED;194}195196/*197* hl_ts_free_objects - handler of the free objects workqueue.198* This function should put refcount to objects that the registration node199* took refcount to them.200* @work: workqueue object pointer201*/202static void hl_ts_free_objects(struct work_struct *work)203{204struct timestamp_reg_work_obj *job =205container_of(work, struct timestamp_reg_work_obj, free_obj);206struct list_head *dynamic_alloc_free_list_head = job->dynamic_alloc_free_obj_head;207struct timestamp_reg_free_node *free_obj, *temp_free_obj;208struct list_head *free_list_head = job->free_obj_head;209210struct hl_device *hdev = job->hdev;211212list_for_each_entry_safe(free_obj, temp_free_obj, free_list_head, free_objects_node) {213dev_dbg(hdev->dev, "About to put refcount to buf (%p) cq_cb(%p)\n",214free_obj->buf,215free_obj->cq_cb);216217hl_mmap_mem_buf_put(free_obj->buf);218hl_cb_put(free_obj->cq_cb);219atomic_set(&free_obj->in_use, 0);220}221222kfree(free_list_head);223224if (dynamic_alloc_free_list_head) {225list_for_each_entry_safe(free_obj, temp_free_obj, dynamic_alloc_free_list_head,226free_objects_node) {227dev_dbg(hdev->dev,228"Dynamic_Alloc list: About to put refcount to buf (%p) cq_cb(%p)\n",229free_obj->buf,230free_obj->cq_cb);231232hl_mmap_mem_buf_put(free_obj->buf);233hl_cb_put(free_obj->cq_cb);234list_del(&free_obj->free_objects_node);235kfree(free_obj);236}237238kfree(dynamic_alloc_free_list_head);239}240241kfree(job);242}243244/*245* This function called with spin_lock of wait_list_lock taken246* This function will set timestamp and delete the registration node from the247* wait_list_lock.248* and since we're protected with spin_lock here, so we cannot just put the refcount249* for the objects here, since the release function may be called and it's also a long250* logic (which might sleep also) that cannot be handled in irq context.251* so here we'll be filling a list with nodes of "put" jobs and then will send this252* list to a dedicated workqueue to do the actual put.253*/254static int handle_registration_node(struct hl_device *hdev, struct hl_user_pending_interrupt *pend,255struct list_head **free_list,256struct list_head **dynamic_alloc_list,257struct hl_user_interrupt *intr)258{259struct hl_ts_free_jobs *ts_free_jobs_data;260struct timestamp_reg_free_node *free_node;261u32 free_node_index;262u64 timestamp;263264ts_free_jobs_data = &intr->ts_free_jobs_data;265free_node_index = ts_free_jobs_data->next_avail_free_node_idx;266267if (!(*free_list)) {268/* Alloc/Init the timestamp registration free objects list */269*free_list = kmalloc(sizeof(struct list_head), GFP_ATOMIC);270if (!(*free_list))271return -ENOMEM;272273INIT_LIST_HEAD(*free_list);274}275276free_node = &ts_free_jobs_data->free_nodes_pool[free_node_index];277if (atomic_cmpxchg(&free_node->in_use, 0, 1)) {278dev_dbg(hdev->dev,279"Timestamp free node pool is full, buff: %p, record: %p, irq: %u\n",280pend->ts_reg_info.buf,281pend,282intr->interrupt_id);283284if (!(*dynamic_alloc_list)) {285*dynamic_alloc_list = kmalloc(sizeof(struct list_head), GFP_ATOMIC);286if (!(*dynamic_alloc_list))287return -ENOMEM;288289INIT_LIST_HEAD(*dynamic_alloc_list);290}291292free_node = kmalloc(sizeof(struct timestamp_reg_free_node), GFP_ATOMIC);293if (!free_node)294return -ENOMEM;295296free_node->dynamic_alloc = 1;297}298299timestamp = ktime_to_ns(intr->timestamp);300301*pend->ts_reg_info.timestamp_kernel_addr = timestamp;302303dev_dbg(hdev->dev, "Irq handle: Timestamp record (%p) ts cb address (%p), interrupt_id: %u\n",304pend, pend->ts_reg_info.timestamp_kernel_addr, intr->interrupt_id);305306list_del(&pend->list_node);307308/* Putting the refcount for ts_buff and cq_cb objects will be handled309* in workqueue context, just add job to free_list.310*/311free_node->buf = pend->ts_reg_info.buf;312free_node->cq_cb = pend->ts_reg_info.cq_cb;313314if (free_node->dynamic_alloc) {315list_add(&free_node->free_objects_node, *dynamic_alloc_list);316} else {317ts_free_jobs_data->next_avail_free_node_idx =318(++free_node_index) % ts_free_jobs_data->free_nodes_length;319list_add(&free_node->free_objects_node, *free_list);320}321322/* Mark TS record as free */323pend->ts_reg_info.in_use = false;324325return 0;326}327328static void handle_user_interrupt_ts_list(struct hl_device *hdev, struct hl_user_interrupt *intr)329{330struct list_head *ts_reg_free_list_head = NULL, *dynamic_alloc_list_head = NULL;331struct hl_user_pending_interrupt *pend, *temp_pend;332struct timestamp_reg_work_obj *job;333bool reg_node_handle_fail = false;334unsigned long flags;335int rc;336337/* For registration nodes:338* As part of handling the registration nodes, we should put refcount to339* some objects. the problem is that we cannot do that under spinlock340* or in irq handler context at all (since release functions are long and341* might sleep), so we will need to handle that part in workqueue context.342* To avoid handling kmalloc failure which compels us rolling back actions343* and move nodes hanged on the free list back to the interrupt ts list344* we always alloc the job of the WQ at the beginning.345*/346job = kmalloc(sizeof(*job), GFP_ATOMIC);347if (!job)348return;349350spin_lock_irqsave(&intr->ts_list_lock, flags);351list_for_each_entry_safe(pend, temp_pend, &intr->ts_list_head, list_node) {352if ((pend->cq_kernel_addr && *(pend->cq_kernel_addr) >= pend->cq_target_value) ||353!pend->cq_kernel_addr) {354if (!reg_node_handle_fail) {355rc = handle_registration_node(hdev, pend,356&ts_reg_free_list_head,357&dynamic_alloc_list_head, intr);358if (rc)359reg_node_handle_fail = true;360}361}362}363spin_unlock_irqrestore(&intr->ts_list_lock, flags);364365if (ts_reg_free_list_head) {366INIT_WORK(&job->free_obj, hl_ts_free_objects);367job->free_obj_head = ts_reg_free_list_head;368job->dynamic_alloc_free_obj_head = dynamic_alloc_list_head;369job->hdev = hdev;370queue_work(hdev->ts_free_obj_wq, &job->free_obj);371} else {372kfree(job);373}374}375376static void handle_user_interrupt_wait_list(struct hl_device *hdev, struct hl_user_interrupt *intr)377{378struct hl_user_pending_interrupt *pend, *temp_pend;379unsigned long flags;380381spin_lock_irqsave(&intr->wait_list_lock, flags);382list_for_each_entry_safe(pend, temp_pend, &intr->wait_list_head, list_node) {383if ((pend->cq_kernel_addr && *(pend->cq_kernel_addr) >= pend->cq_target_value) ||384!pend->cq_kernel_addr) {385/* Handle wait target value node */386pend->fence.timestamp = intr->timestamp;387complete_all(&pend->fence.completion);388}389}390spin_unlock_irqrestore(&intr->wait_list_lock, flags);391}392393static void handle_tpc_interrupt(struct hl_device *hdev)394{395u64 event_mask;396u32 flags;397398event_mask = HL_NOTIFIER_EVENT_TPC_ASSERT |399HL_NOTIFIER_EVENT_USER_ENGINE_ERR |400HL_NOTIFIER_EVENT_DEVICE_RESET;401402flags = HL_DRV_RESET_DELAY;403404dev_err_ratelimited(hdev->dev, "Received TPC assert\n");405hl_device_cond_reset(hdev, flags, event_mask);406}407408static void handle_unexpected_user_interrupt(struct hl_device *hdev)409{410dev_err_ratelimited(hdev->dev, "Received unexpected user error interrupt\n");411}412413/**414* hl_irq_user_interrupt_handler - irq handler for user interrupts.415*416* @irq: irq number417* @arg: pointer to user interrupt structure418*/419irqreturn_t hl_irq_user_interrupt_handler(int irq, void *arg)420{421struct hl_user_interrupt *user_int = arg;422struct hl_device *hdev = user_int->hdev;423424user_int->timestamp = ktime_get();425switch (user_int->type) {426case HL_USR_INTERRUPT_CQ:427/* First handle user waiters threads */428handle_user_interrupt_wait_list(hdev, &hdev->common_user_cq_interrupt);429handle_user_interrupt_wait_list(hdev, user_int);430431/* Second handle user timestamp registrations */432handle_user_interrupt_ts_list(hdev, &hdev->common_user_cq_interrupt);433handle_user_interrupt_ts_list(hdev, user_int);434break;435case HL_USR_INTERRUPT_DECODER:436handle_user_interrupt_wait_list(hdev, &hdev->common_decoder_interrupt);437438/* Handle decoder interrupt registered on this specific irq */439handle_user_interrupt_wait_list(hdev, user_int);440break;441default:442break;443}444445return IRQ_HANDLED;446}447448/**449* hl_irq_user_interrupt_thread_handler - irq thread handler for user interrupts.450* This function is invoked by threaded irq mechanism451*452* @irq: irq number453* @arg: pointer to user interrupt structure454*455*/456irqreturn_t hl_irq_user_interrupt_thread_handler(int irq, void *arg)457{458struct hl_user_interrupt *user_int = arg;459struct hl_device *hdev = user_int->hdev;460461user_int->timestamp = ktime_get();462switch (user_int->type) {463case HL_USR_INTERRUPT_TPC:464handle_tpc_interrupt(hdev);465break;466case HL_USR_INTERRUPT_UNEXPECTED:467handle_unexpected_user_interrupt(hdev);468break;469default:470break;471}472473return IRQ_HANDLED;474}475476irqreturn_t hl_irq_eq_error_interrupt_thread_handler(int irq, void *arg)477{478u64 event_mask = HL_NOTIFIER_EVENT_DEVICE_RESET | HL_NOTIFIER_EVENT_DEVICE_UNAVAILABLE;479struct hl_device *hdev = arg;480481dev_err(hdev->dev, "EQ error interrupt received\n");482483hl_device_cond_reset(hdev, HL_DRV_RESET_HARD, event_mask);484485return IRQ_HANDLED;486}487488/**489* hl_irq_handler_eq - irq handler for event queue490*491* @irq: irq number492* @arg: pointer to event queue structure493*494*/495irqreturn_t hl_irq_handler_eq(int irq, void *arg)496{497struct hl_eq *eq = arg;498struct hl_device *hdev = eq->hdev;499struct hl_eq_entry *eq_entry;500struct hl_eq_entry *eq_base;501struct hl_eqe_work *handle_eqe_work;502bool entry_ready;503u32 cur_eqe, ctl;504u16 cur_eqe_index, event_type;505506eq_base = eq->kernel_address;507508while (1) {509cur_eqe = le32_to_cpu(eq_base[eq->ci].hdr.ctl);510entry_ready = !!FIELD_GET(EQ_CTL_READY_MASK, cur_eqe);511512if (!entry_ready)513break;514515cur_eqe_index = FIELD_GET(EQ_CTL_INDEX_MASK, cur_eqe);516if ((hdev->event_queue.check_eqe_index) &&517(((eq->prev_eqe_index + 1) & EQ_CTL_INDEX_MASK) != cur_eqe_index)) {518dev_err(hdev->dev,519"EQE %#x in queue is ready but index does not match %d!=%d",520cur_eqe,521((eq->prev_eqe_index + 1) & EQ_CTL_INDEX_MASK),522cur_eqe_index);523break;524}525526eq->prev_eqe_index++;527528eq_entry = &eq_base[eq->ci];529530/*531* Make sure we read EQ entry contents after we've532* checked the ownership bit.533*/534dma_rmb();535536if (hdev->disabled && !hdev->reset_info.in_compute_reset) {537ctl = le32_to_cpu(eq_entry->hdr.ctl);538event_type = ((ctl & EQ_CTL_EVENT_TYPE_MASK) >> EQ_CTL_EVENT_TYPE_SHIFT);539dev_warn(hdev->dev,540"Device disabled but received an EQ event (%u)\n", event_type);541goto skip_irq;542}543544handle_eqe_work = kmalloc(sizeof(*handle_eqe_work), GFP_ATOMIC);545if (handle_eqe_work) {546INIT_WORK(&handle_eqe_work->eq_work, irq_handle_eqe);547handle_eqe_work->hdev = hdev;548549memcpy(&handle_eqe_work->eq_entry, eq_entry,550sizeof(*eq_entry));551552queue_work(hdev->eq_wq, &handle_eqe_work->eq_work);553}554skip_irq:555/* Clear EQ entry ready bit */556eq_entry->hdr.ctl =557cpu_to_le32(le32_to_cpu(eq_entry->hdr.ctl) &558~EQ_CTL_READY_MASK);559560eq->ci = hl_eq_inc_ptr(eq->ci);561562hdev->asic_funcs->update_eq_ci(hdev, eq->ci);563}564565return IRQ_HANDLED;566}567568/**569* hl_irq_handler_dec_abnrm - Decoder error interrupt handler570* @irq: IRQ number571* @arg: pointer to decoder structure.572*/573irqreturn_t hl_irq_handler_dec_abnrm(int irq, void *arg)574{575struct hl_dec *dec = arg;576577schedule_work(&dec->abnrm_intr_work);578579return IRQ_HANDLED;580}581582/**583* hl_cq_init - main initialization function for an cq object584*585* @hdev: pointer to device structure586* @q: pointer to cq structure587* @hw_queue_id: The H/W queue ID this completion queue belongs to588* HL_INVALID_QUEUE if cq is not attached to any specific queue589*590* Allocate dma-able memory for the completion queue and initialize fields591* Returns 0 on success592*/593int hl_cq_init(struct hl_device *hdev, struct hl_cq *q, u32 hw_queue_id)594{595void *p;596597p = hl_asic_dma_alloc_coherent(hdev, HL_CQ_SIZE_IN_BYTES, &q->bus_address,598GFP_KERNEL | __GFP_ZERO);599if (!p)600return -ENOMEM;601602q->hdev = hdev;603q->kernel_address = p;604q->hw_queue_id = hw_queue_id;605q->ci = 0;606q->pi = 0;607608atomic_set(&q->free_slots_cnt, HL_CQ_LENGTH);609610return 0;611}612613/**614* hl_cq_fini - destroy completion queue615*616* @hdev: pointer to device structure617* @q: pointer to cq structure618*619* Free the completion queue memory620*/621void hl_cq_fini(struct hl_device *hdev, struct hl_cq *q)622{623hl_asic_dma_free_coherent(hdev, HL_CQ_SIZE_IN_BYTES, q->kernel_address, q->bus_address);624}625626void hl_cq_reset(struct hl_device *hdev, struct hl_cq *q)627{628q->ci = 0;629q->pi = 0;630631atomic_set(&q->free_slots_cnt, HL_CQ_LENGTH);632633/*634* It's not enough to just reset the PI/CI because the H/W may have635* written valid completion entries before it was halted and therefore636* we need to clean the actual queues so we won't process old entries637* when the device is operational again638*/639640memset(q->kernel_address, 0, HL_CQ_SIZE_IN_BYTES);641}642643/**644* hl_eq_init - main initialization function for an event queue object645*646* @hdev: pointer to device structure647* @q: pointer to eq structure648*649* Allocate dma-able memory for the event queue and initialize fields650* Returns 0 on success651*/652int hl_eq_init(struct hl_device *hdev, struct hl_eq *q)653{654u32 size = hdev->asic_prop.fw_event_queue_size ? : HL_EQ_SIZE_IN_BYTES;655void *p;656657p = hl_cpu_accessible_dma_pool_alloc(hdev, size, &q->bus_address);658if (!p)659return -ENOMEM;660661q->hdev = hdev;662q->kernel_address = p;663q->size = size;664q->ci = 0;665q->prev_eqe_index = 0;666667return 0;668}669670/**671* hl_eq_fini - destroy event queue672*673* @hdev: pointer to device structure674* @q: pointer to eq structure675*676* Free the event queue memory677*/678void hl_eq_fini(struct hl_device *hdev, struct hl_eq *q)679{680flush_workqueue(hdev->eq_wq);681682hl_cpu_accessible_dma_pool_free(hdev, q->size, q->kernel_address);683}684685void hl_eq_reset(struct hl_device *hdev, struct hl_eq *q)686{687q->ci = 0;688q->prev_eqe_index = 0;689690/*691* It's not enough to just reset the PI/CI because the H/W may have692* written valid completion entries before it was halted and therefore693* we need to clean the actual queues so we won't process old entries694* when the device is operational again695*/696697memset(q->kernel_address, 0, q->size);698}699700void hl_eq_dump(struct hl_device *hdev, struct hl_eq *q)701{702u32 eq_length, eqe_size, ctl, ready, mode, type, index;703struct hl_eq_header *hdr;704u8 *ptr;705int i;706707eq_length = HL_EQ_LENGTH;708eqe_size = q->size / HL_EQ_LENGTH;709710dev_info(hdev->dev, "Contents of EQ entries headers:\n");711712for (i = 0, ptr = q->kernel_address ; i < eq_length ; ++i, ptr += eqe_size) {713hdr = (struct hl_eq_header *) ptr;714ctl = le32_to_cpu(hdr->ctl);715ready = FIELD_GET(EQ_CTL_READY_MASK, ctl);716mode = FIELD_GET(EQ_CTL_EVENT_MODE_MASK, ctl);717type = FIELD_GET(EQ_CTL_EVENT_TYPE_MASK, ctl);718index = FIELD_GET(EQ_CTL_INDEX_MASK, ctl);719720dev_info(hdev->dev, "%02u: %#010x [ready: %u, mode %u, type %04u, index %05u]\n",721i, ctl, ready, mode, type, index);722}723}724725726