habanalabs: enable all IRQs for user interrupt support
authorOfir Bitton <obitton@habana.ai>
Tue, 12 Jan 2021 12:43:09 +0000 (14:43 +0200)
committerOded Gabbay <ogabbay@kernel.org>
Fri, 9 Apr 2021 11:09:22 +0000 (14:09 +0300)
In order to support user interrupts, driver must enable all MSI-X
interrupts for any case user will trigger them. We differentiate
between a valid user interrupt and a non valid one.

Signed-off-by: Ofir Bitton <obitton@habana.ai>
Reviewed-by: Oded Gabbay <ogabbay@kernel.org>
Signed-off-by: Oded Gabbay <ogabbay@kernel.org>
drivers/misc/habanalabs/common/device.c
drivers/misc/habanalabs/common/habanalabs.h
drivers/misc/habanalabs/common/irq.c

index 17e9e205557b4a5b2240cf4faea0d41ae49bb600..24e3ea15b2b9430983708b9f9ce8efe822e774c0 100644 (file)
@@ -1251,7 +1251,7 @@ out_err:
  */
 int hl_device_init(struct hl_device *hdev, struct class *hclass)
 {
-       int i, rc, cq_cnt, cq_ready_cnt;
+       int i, rc, cq_cnt, user_interrupt_cnt, cq_ready_cnt;
        char *name;
        bool add_cdev_sysfs_on_err = false;
 
@@ -1340,6 +1340,19 @@ int hl_device_init(struct hl_device *hdev, struct class *hclass)
                hdev->completion_queue[i].cq_idx = i;
        }
 
+       user_interrupt_cnt = hdev->asic_prop.user_interrupt_count;
+
+       if (user_interrupt_cnt) {
+               hdev->user_interrupt = kcalloc(user_interrupt_cnt,
+                               sizeof(*hdev->user_interrupt),
+                               GFP_KERNEL);
+
+               if (!hdev->user_interrupt) {
+                       rc = -ENOMEM;
+                       goto cq_fini;
+               }
+       }
+
        /*
         * Initialize the event queue. Must be done before hw_init,
         * because there the address of the event queue is being
@@ -1348,7 +1361,7 @@ int hl_device_init(struct hl_device *hdev, struct class *hclass)
        rc = hl_eq_init(hdev, &hdev->event_queue);
        if (rc) {
                dev_err(hdev->dev, "failed to initialize event queue\n");
-               goto cq_fini;
+               goto user_interrupts_fini;
        }
 
        /* MMU S/W must be initialized before kernel context is created */
@@ -1486,6 +1499,8 @@ mmu_fini:
        hl_mmu_fini(hdev);
 eq_fini:
        hl_eq_fini(hdev, &hdev->event_queue);
+user_interrupts_fini:
+       kfree(hdev->user_interrupt);
 cq_fini:
        for (i = 0 ; i < cq_ready_cnt ; i++)
                hl_cq_fini(hdev, &hdev->completion_queue[i]);
@@ -1625,6 +1640,7 @@ void hl_device_fini(struct hl_device *hdev)
        for (i = 0 ; i < hdev->asic_prop.completion_queues_count ; i++)
                hl_cq_fini(hdev, &hdev->completion_queue[i]);
        kfree(hdev->completion_queue);
+       kfree(hdev->user_interrupt);
 
        hl_hw_queues_destroy(hdev);
 
index 8eb22f0e459c135f69c523381a205ffabca04016..7ffd4b84ae9071bcbda597c2e0667d314a885be2 100644 (file)
@@ -412,6 +412,7 @@ struct hl_mmu_properties {
  * @first_available_user_msix_interrupt: first available msix interrupt
  *                                       reserved for the user
  * @first_available_cq: first available CQ for the user.
+ * @user_interrupt_count: number of user interrupts.
  * @tpc_enabled_mask: which TPCs are enabled.
  * @completion_queues_count: number of completion queues.
  * @fw_security_disabled: true if security measures are disabled in firmware,
@@ -475,6 +476,7 @@ struct asic_fixed_properties {
        u16                             first_available_user_mon[HL_MAX_DCORES];
        u16                             first_available_user_msix_interrupt;
        u16                             first_available_cq[HL_MAX_DCORES];
+       u16                             user_interrupt_count;
        u8                              tpc_enabled_mask;
        u8                              completion_queues_count;
        u8                              fw_security_disabled;
@@ -689,6 +691,16 @@ struct hl_cq {
        atomic_t                free_slots_cnt;
 };
 
+/**
+ * struct hl_user_interrupt - holds user interrupt information
+ * @hdev: pointer to the device structure
+ * @interrupt_id: msix interrupt id
+ */
+struct hl_user_interrupt {
+       struct hl_device        *hdev;
+       u32                     interrupt_id;
+};
+
 /**
  * struct hl_eq - describes the event queue (single one per device)
  * @hdev: pointer to the device structure
@@ -1821,6 +1833,7 @@ struct hl_mmu_funcs {
  * @asic_name: ASIC specific name.
  * @asic_type: ASIC specific type.
  * @completion_queue: array of hl_cq.
+ * @user_interrupt: array of hl_user_interrupt.
  * @cq_wq: work queues of completion queues for executing work in process
  *         context.
  * @eq_wq: work queue of event queue for executing work in process context.
@@ -1937,6 +1950,7 @@ struct hl_device {
        char                            status[HL_DEV_STS_MAX][HL_STR_MAX];
        enum hl_asic_type               asic_type;
        struct hl_cq                    *completion_queue;
+       struct hl_user_interrupt        *user_interrupt;
        struct workqueue_struct         **cq_wq;
        struct workqueue_struct         *eq_wq;
        struct hl_ctx                   *kernel_ctx;
@@ -2158,6 +2172,8 @@ void hl_cq_reset(struct hl_device *hdev, struct hl_cq *q);
 void hl_eq_reset(struct hl_device *hdev, struct hl_eq *q);
 irqreturn_t hl_irq_handler_cq(int irq, void *arg);
 irqreturn_t hl_irq_handler_eq(int irq, void *arg);
+irqreturn_t hl_irq_handler_user_cq(int irq, void *arg);
+irqreturn_t hl_irq_handler_default(int irq, void *arg);
 u32 hl_cq_inc_ptr(u32 ptr);
 
 int hl_asid_init(struct hl_device *hdev);
index 44a0522b59b9507e3b4144719a8a9d07bf26c4d2..c8b5dc07e6caef262ef25b9115d15df72d8ba282 100644 (file)
@@ -137,6 +137,46 @@ irqreturn_t hl_irq_handler_cq(int irq, void *arg)
        return IRQ_HANDLED;
 }
 
+/**
+ * hl_irq_handler_user_cq - irq handler for user completion queues
+ *
+ * @irq: irq number
+ * @arg: pointer to user interrupt structure
+ *
+ */
+irqreturn_t hl_irq_handler_user_cq(int irq, void *arg)
+{
+       struct hl_user_interrupt *user_cq = arg;
+       struct hl_device *hdev = user_cq->hdev;
+       u32 interrupt_id = user_cq->interrupt_id;
+
+       dev_info(hdev->dev,
+               "got user completion interrupt id %u",
+               interrupt_id);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * hl_irq_handler_default - default irq handler
+ *
+ * @irq: irq number
+ * @arg: pointer to user interrupt structure
+ *
+ */
+irqreturn_t hl_irq_handler_default(int irq, void *arg)
+{
+       struct hl_user_interrupt *user_interrupt = arg;
+       struct hl_device *hdev = user_interrupt->hdev;
+       u32 interrupt_id = user_interrupt->interrupt_id;
+
+       dev_err(hdev->dev,
+               "got invalid user interrupt %u",
+               interrupt_id);
+
+       return IRQ_HANDLED;
+}
+
 /**
  * hl_irq_handler_eq - irq handler for event queue
  *