Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

dmaengine: idxd: add interrupt handle request and release support

DSA spec states that when Request Interrupt Handle and Release Interrupt
Handle command bits are set in the CMDCAP register, these device commands
must be supported by the driver.

The interrupt handle is programmed in a descriptor. When Request Interrupt
Handle is not supported, the interrupt handle is the index of the desired
entry in the MSI-X table. When the command is supported, driver must use
the command to obtain a handle to be programmed in the submitted
descriptor.

A requested handle may be revoked. After the handle is revoked, any use of
the handle will result in Invalid Interrupt Handle error.

Signed-off-by: Dave Jiang <dave.jiang@intel.com>
Link: https://lore.kernel.org/r/161894439422.3202472.17579543737810265471.stgit@djiang5-desk3.ch.intel.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>

authored by

Dave Jiang and committed by
Vinod Koul
eb15e715 8c66bbdc

+176 -9
+71
drivers/dma/idxd/device.c
··· 598 598 dev_dbg(dev, "pasid %d drained\n", pasid); 599 599 } 600 600 601 + int idxd_device_request_int_handle(struct idxd_device *idxd, int idx, int *handle, 602 + enum idxd_interrupt_type irq_type) 603 + { 604 + struct device *dev = &idxd->pdev->dev; 605 + u32 operand, status; 606 + 607 + if (!(idxd->hw.cmd_cap & BIT(IDXD_CMD_REQUEST_INT_HANDLE))) 608 + return -EOPNOTSUPP; 609 + 610 + dev_dbg(dev, "get int handle, idx %d\n", idx); 611 + 612 + operand = idx & GENMASK(15, 0); 613 + if (irq_type == IDXD_IRQ_IMS) 614 + operand |= CMD_INT_HANDLE_IMS; 615 + 616 + dev_dbg(dev, "cmd: %u operand: %#x\n", IDXD_CMD_REQUEST_INT_HANDLE, operand); 617 + 618 + idxd_cmd_exec(idxd, IDXD_CMD_REQUEST_INT_HANDLE, operand, &status); 619 + 620 + if ((status & IDXD_CMDSTS_ERR_MASK) != IDXD_CMDSTS_SUCCESS) { 621 + dev_dbg(dev, "request int handle failed: %#x\n", status); 622 + return -ENXIO; 623 + } 624 + 625 + *handle = (status >> IDXD_CMDSTS_RES_SHIFT) & GENMASK(15, 0); 626 + 627 + dev_dbg(dev, "int handle acquired: %u\n", *handle); 628 + return 0; 629 + } 630 + 631 + int idxd_device_release_int_handle(struct idxd_device *idxd, int handle, 632 + enum idxd_interrupt_type irq_type) 633 + { 634 + struct device *dev = &idxd->pdev->dev; 635 + u32 operand, status; 636 + union idxd_command_reg cmd; 637 + unsigned long flags; 638 + 639 + if (!(idxd->hw.cmd_cap & BIT(IDXD_CMD_RELEASE_INT_HANDLE))) 640 + return -EOPNOTSUPP; 641 + 642 + dev_dbg(dev, "release int handle, handle %d\n", handle); 643 + 644 + memset(&cmd, 0, sizeof(cmd)); 645 + operand = handle & GENMASK(15, 0); 646 + 647 + if (irq_type == IDXD_IRQ_IMS) 648 + operand |= CMD_INT_HANDLE_IMS; 649 + 650 + cmd.cmd = IDXD_CMD_RELEASE_INT_HANDLE; 651 + cmd.operand = operand; 652 + 653 + dev_dbg(dev, "cmd: %u operand: %#x\n", IDXD_CMD_RELEASE_INT_HANDLE, operand); 654 + 655 + spin_lock_irqsave(&idxd->dev_lock, flags); 656 + iowrite32(cmd.bits, idxd->reg_base + IDXD_CMD_OFFSET); 657 + 658 + while (ioread32(idxd->reg_base + IDXD_CMDSTS_OFFSET) & IDXD_CMDSTS_ACTIVE) 659 + cpu_relax(); 660 + status = ioread32(idxd->reg_base + IDXD_CMDSTS_OFFSET); 661 + spin_unlock_irqrestore(&idxd->dev_lock, flags); 662 + 663 + if ((status & IDXD_CMDSTS_ERR_MASK) != IDXD_CMDSTS_SUCCESS) { 664 + dev_dbg(dev, "release int handle failed: %#x\n", status); 665 + return -ENXIO; 666 + } 667 + 668 + dev_dbg(dev, "int handle released.\n"); 669 + return 0; 670 + } 671 + 601 672 /* Device configuration bits */ 602 673 void idxd_msix_perm_setup(struct idxd_device *idxd) 603 674 {
+13
drivers/dma/idxd/idxd.h
··· 160 160 union group_cap_reg group_cap; 161 161 union engine_cap_reg engine_cap; 162 162 struct opcap opcap; 163 + u32 cmd_cap; 163 164 }; 164 165 165 166 enum idxd_device_state { ··· 238 237 struct idxd_dma_dev *idxd_dma; 239 238 struct workqueue_struct *wq; 240 239 struct work_struct work; 240 + 241 + int *int_handles; 241 242 }; 242 243 243 244 /* IDXD software descriptor */ ··· 259 256 struct list_head list; 260 257 int id; 261 258 int cpu; 259 + unsigned int vector; 262 260 struct idxd_wq *wq; 263 261 }; 264 262 ··· 334 330 IDXD_PORTAL_LIMITED, 335 331 }; 336 332 333 + enum idxd_interrupt_type { 334 + IDXD_IRQ_MSIX = 0, 335 + IDXD_IRQ_IMS, 336 + }; 337 + 337 338 static inline int idxd_get_wq_portal_offset(enum idxd_portal_prot prot) 338 339 { 339 340 return prot * 0x1000; ··· 394 385 void idxd_device_wqs_clear_state(struct idxd_device *idxd); 395 386 void idxd_device_drain_pasid(struct idxd_device *idxd, int pasid); 396 387 int idxd_device_load_config(struct idxd_device *idxd); 388 + int idxd_device_request_int_handle(struct idxd_device *idxd, int idx, int *handle, 389 + enum idxd_interrupt_type irq_type); 390 + int idxd_device_release_int_handle(struct idxd_device *idxd, int handle, 391 + enum idxd_interrupt_type irq_type); 397 392 398 393 /* work queue control */ 399 394 int idxd_wq_alloc_resources(struct idxd_wq *wq);
+55 -1
drivers/dma/idxd/init.c
··· 125 125 dev_err(dev, "Failed to allocate irq %d.\n", irq_entry->vector); 126 126 goto err_wq_irqs; 127 127 } 128 + 128 129 dev_dbg(dev, "Allocated idxd-msix %d for vector %d\n", i, irq_entry->vector); 130 + if (idxd->hw.cmd_cap & BIT(IDXD_CMD_REQUEST_INT_HANDLE)) { 131 + /* 132 + * The MSIX vector enumeration starts at 1 with vector 0 being the 133 + * misc interrupt that handles non I/O completion events. The 134 + * interrupt handles are for IMS enumeration on guest. The misc 135 + * interrupt vector does not require a handle and therefore we start 136 + * the int_handles at index 0. Since 'i' starts at 1, the first 137 + * int_handles index will be 0. 138 + */ 139 + rc = idxd_device_request_int_handle(idxd, i, &idxd->int_handles[i - 1], 140 + IDXD_IRQ_MSIX); 141 + if (rc < 0) { 142 + free_irq(irq_entry->vector, irq_entry); 143 + goto err_wq_irqs; 144 + } 145 + dev_dbg(dev, "int handle requested: %u\n", idxd->int_handles[i - 1]); 146 + } 129 147 } 130 148 131 149 idxd_unmask_error_interrupts(idxd); ··· 154 136 while (--i >= 0) { 155 137 irq_entry = &idxd->irq_entries[i]; 156 138 free_irq(irq_entry->vector, irq_entry); 139 + if (i != 0) 140 + idxd_device_release_int_handle(idxd, 141 + idxd->int_handles[i], IDXD_IRQ_MSIX); 157 142 } 158 143 err_misc_irq: 159 144 /* Disable error interrupt generation */ ··· 309 288 310 289 init_waitqueue_head(&idxd->cmd_waitq); 311 290 291 + if (idxd->hw.cmd_cap & BIT(IDXD_CMD_REQUEST_INT_HANDLE)) { 292 + idxd->int_handles = devm_kcalloc(dev, idxd->max_wqs, sizeof(int), GFP_KERNEL); 293 + if (!idxd->int_handles) 294 + return -ENOMEM; 295 + } 296 + 312 297 rc = idxd_setup_wqs(idxd); 313 298 if (rc < 0) 314 - return rc; 299 + goto err_wqs; 315 300 316 301 rc = idxd_setup_engines(idxd); 317 302 if (rc < 0) ··· 344 317 err_engine: 345 318 for (i = 0; i < idxd->max_wqs; i++) 346 319 put_device(&idxd->wqs[i]->conf_dev); 320 + err_wqs: 321 + kfree(idxd->int_handles); 347 322 return rc; 348 323 } 349 324 ··· 374 345 /* reading generic capabilities */ 375 346 idxd->hw.gen_cap.bits = ioread64(idxd->reg_base + IDXD_GENCAP_OFFSET); 376 347 dev_dbg(dev, "gen_cap: %#llx\n", idxd->hw.gen_cap.bits); 348 + 349 + if (idxd->hw.gen_cap.cmd_cap) { 350 + idxd->hw.cmd_cap = ioread32(idxd->reg_base + IDXD_CMDCAP_OFFSET); 351 + dev_dbg(dev, "cmd_cap: %#x\n", idxd->hw.cmd_cap); 352 + } 353 + 377 354 idxd->max_xfer_bytes = 1ULL << idxd->hw.gen_cap.max_xfer_shift; 378 355 dev_dbg(dev, "max xfer size: %llu bytes\n", idxd->max_xfer_bytes); 379 356 idxd->max_batch_size = 1U << idxd->hw.gen_cap.max_batch_shift; ··· 639 604 } 640 605 } 641 606 607 + static void idxd_release_int_handles(struct idxd_device *idxd) 608 + { 609 + struct device *dev = &idxd->pdev->dev; 610 + int i, rc; 611 + 612 + for (i = 0; i < idxd->num_wq_irqs; i++) { 613 + if (idxd->hw.cmd_cap & BIT(IDXD_CMD_RELEASE_INT_HANDLE)) { 614 + rc = idxd_device_release_int_handle(idxd, idxd->int_handles[i], 615 + IDXD_IRQ_MSIX); 616 + if (rc < 0) 617 + dev_warn(dev, "irq handle %d release failed\n", 618 + idxd->int_handles[i]); 619 + else 620 + dev_dbg(dev, "int handle requested: %u\n", idxd->int_handles[i]); 621 + } 622 + } 623 + } 624 + 642 625 static void idxd_shutdown(struct pci_dev *pdev) 643 626 { 644 627 struct idxd_device *idxd = pci_get_drvdata(pdev); ··· 683 630 } 684 631 685 632 idxd_msix_perm_clear(idxd); 633 + idxd_release_int_handles(idxd); 686 634 pci_free_irq_vectors(pdev); 687 635 pci_iounmap(pdev, idxd->reg_base); 688 636 pci_disable_device(pdev);
+8 -1
drivers/dma/idxd/registers.h
··· 24 24 u64 overlap_copy:1; 25 25 u64 cache_control_mem:1; 26 26 u64 cache_control_cache:1; 27 + u64 cmd_cap:1; 27 28 u64 rsvd:3; 28 - u64 int_handle_req:1; 29 29 u64 dest_readback:1; 30 30 u64 drain_readback:1; 31 31 u64 rsvd2:6; ··· 180 180 IDXD_CMD_DRAIN_PASID, 181 181 IDXD_CMD_ABORT_PASID, 182 182 IDXD_CMD_REQUEST_INT_HANDLE, 183 + IDXD_CMD_RELEASE_INT_HANDLE, 183 184 }; 185 + 186 + #define CMD_INT_HANDLE_IMS 0x10000 184 187 185 188 #define IDXD_CMDSTS_OFFSET 0xa8 186 189 union cmdsts_reg { ··· 196 193 u32 bits; 197 194 } __packed; 198 195 #define IDXD_CMDSTS_ACTIVE 0x80000000 196 + #define IDXD_CMDSTS_ERR_MASK 0xff 197 + #define IDXD_CMDSTS_RES_SHIFT 8 199 198 200 199 enum idxd_cmdsts_err { 201 200 IDXD_CMDSTS_SUCCESS = 0, ··· 232 227 IDXD_CMDSTS_ERR_INVAL_INT_IDX = 0x41, 233 228 IDXD_CMDSTS_ERR_NO_HANDLE, 234 229 }; 230 + 231 + #define IDXD_CMDCAP_OFFSET 0xb0 235 232 236 233 #define IDXD_SWERR_OFFSET 0xc0 237 234 #define IDXD_SWERR_VALID 0x00000001
+28 -7
drivers/dma/idxd/submit.c
··· 22 22 desc->hw->pasid = idxd->pasid; 23 23 24 24 /* 25 - * Descriptor completion vectors are 1-8 for MSIX. We will round 26 - * robin through the 8 vectors. 25 + * Descriptor completion vectors are 1...N for MSIX. We will round 26 + * robin through the N vectors. 27 27 */ 28 28 wq->vec_ptr = (wq->vec_ptr % idxd->num_wq_irqs) + 1; 29 - desc->hw->int_handle = wq->vec_ptr; 29 + if (!idxd->int_handles) { 30 + desc->hw->int_handle = wq->vec_ptr; 31 + } else { 32 + desc->vector = wq->vec_ptr; 33 + /* 34 + * int_handles are only for descriptor completion. However for device 35 + * MSIX enumeration, vec 0 is used for misc interrupts. Therefore even 36 + * though we are rotating through 1...N for descriptor interrupts, we 37 + * need to acqurie the int_handles from 0..N-1. 38 + */ 39 + desc->hw->int_handle = idxd->int_handles[desc->vector - 1]; 40 + } 41 + 30 42 return desc; 31 43 } 32 44 ··· 91 79 int idxd_submit_desc(struct idxd_wq *wq, struct idxd_desc *desc) 92 80 { 93 81 struct idxd_device *idxd = wq->idxd; 94 - int vec = desc->hw->int_handle; 95 82 void __iomem *portal; 96 83 int rc; 97 84 ··· 128 117 * Pending the descriptor to the lockless list for the irq_entry 129 118 * that we designated the descriptor to. 130 119 */ 131 - if (desc->hw->flags & IDXD_OP_FLAG_RCI) 132 - llist_add(&desc->llnode, 133 - &idxd->irq_entries[vec].pending_llist); 120 + if (desc->hw->flags & IDXD_OP_FLAG_RCI) { 121 + int vec; 122 + 123 + /* 124 + * If the driver is on host kernel, it would be the value 125 + * assigned to interrupt handle, which is index for MSIX 126 + * vector. If it's guest then can't use the int_handle since 127 + * that is the index to IMS for the entire device. The guest 128 + * device local index will be used. 129 + */ 130 + vec = !idxd->int_handles ? desc->hw->int_handle : desc->vector; 131 + llist_add(&desc->llnode, &idxd->irq_entries[vec].pending_llist); 132 + } 134 133 135 134 return 0; 136 135 }
+1
drivers/dma/idxd/sysfs.c
··· 1600 1600 kfree(idxd->wqs); 1601 1601 kfree(idxd->engines); 1602 1602 kfree(idxd->irq_entries); 1603 + kfree(idxd->int_handles); 1603 1604 ida_free(&idxd_ida, idxd->id); 1604 1605 kfree(idxd); 1605 1606 }