📄 ioat_dma.c
字号:
static void ioat_dma_dependency_added(struct dma_chan *chan){ struct ioat_dma_chan *ioat_chan = to_ioat_chan(chan); spin_lock_bh(&ioat_chan->desc_lock); if (ioat_chan->pending == 0) { spin_unlock_bh(&ioat_chan->desc_lock); ioat_dma_memcpy_cleanup(ioat_chan); } else spin_unlock_bh(&ioat_chan->desc_lock);}/** * ioat_dma_is_complete - poll the status of a IOAT DMA transaction * @chan: IOAT DMA channel handle * @cookie: DMA transaction identifier * @done: if not %NULL, updated with last completed transaction * @used: if not %NULL, updated with last used transaction */static enum dma_status ioat_dma_is_complete(struct dma_chan *chan, dma_cookie_t cookie, dma_cookie_t *done, dma_cookie_t *used){ struct ioat_dma_chan *ioat_chan = to_ioat_chan(chan); dma_cookie_t last_used; dma_cookie_t last_complete; enum dma_status ret; last_used = chan->cookie; last_complete = ioat_chan->completed_cookie; if (done) *done = last_complete; if (used) *used = last_used; ret = dma_async_is_complete(cookie, last_complete, last_used); if (ret == DMA_SUCCESS) return ret; ioat_dma_memcpy_cleanup(ioat_chan); last_used = chan->cookie; last_complete = ioat_chan->completed_cookie; if (done) *done = last_complete; if (used) *used = last_used; return dma_async_is_complete(cookie, last_complete, last_used);}static void ioat_dma_start_null_desc(struct ioat_dma_chan *ioat_chan){ struct ioat_desc_sw *desc; spin_lock_bh(&ioat_chan->desc_lock); desc = ioat_dma_get_next_descriptor(ioat_chan); desc->hw->ctl = IOAT_DMA_DESCRIPTOR_NUL | IOAT_DMA_DESCRIPTOR_CTL_INT_GN | IOAT_DMA_DESCRIPTOR_CTL_CP_STS; desc->hw->size = 0; desc->hw->src_addr = 0; desc->hw->dst_addr = 0; desc->async_tx.ack = 1; switch (ioat_chan->device->version) { case IOAT_VER_1_2: desc->hw->next = 0; list_add_tail(&desc->node, &ioat_chan->used_desc); writel(((u64) desc->async_tx.phys) & 0x00000000FFFFFFFF, ioat_chan->reg_base + IOAT1_CHAINADDR_OFFSET_LOW); writel(((u64) desc->async_tx.phys) >> 32, ioat_chan->reg_base + IOAT1_CHAINADDR_OFFSET_HIGH); writeb(IOAT_CHANCMD_START, ioat_chan->reg_base + IOAT_CHANCMD_OFFSET(ioat_chan->device->version)); break; case IOAT_VER_2_0: writel(((u64) desc->async_tx.phys) & 0x00000000FFFFFFFF, ioat_chan->reg_base + IOAT2_CHAINADDR_OFFSET_LOW); writel(((u64) desc->async_tx.phys) >> 32, ioat_chan->reg_base + IOAT2_CHAINADDR_OFFSET_HIGH); ioat_chan->dmacount++; __ioat2_dma_memcpy_issue_pending(ioat_chan); break; } spin_unlock_bh(&ioat_chan->desc_lock);}/* * Perform a IOAT transaction to verify the HW works. */#define IOAT_TEST_SIZE 2000static void ioat_dma_test_callback(void *dma_async_param){ printk(KERN_ERR "ioatdma: ioat_dma_test_callback(%p)\n", dma_async_param);}/** * ioat_dma_self_test - Perform a IOAT transaction to verify the HW works. * @device: device to be tested */static int ioat_dma_self_test(struct ioatdma_device *device){ int i; u8 *src; u8 *dest; struct dma_chan *dma_chan; struct dma_async_tx_descriptor *tx; dma_addr_t addr; dma_cookie_t cookie; int err = 0; src = kzalloc(sizeof(u8) * IOAT_TEST_SIZE, GFP_KERNEL); if (!src) return -ENOMEM; dest = kzalloc(sizeof(u8) * IOAT_TEST_SIZE, GFP_KERNEL); if (!dest) { kfree(src); return -ENOMEM; } /* Fill in src buffer */ for (i = 0; i < IOAT_TEST_SIZE; i++) src[i] = (u8)i; /* Start copy, using first DMA channel */ dma_chan = container_of(device->common.channels.next, struct dma_chan, device_node); if (device->common.device_alloc_chan_resources(dma_chan) < 1) { dev_err(&device->pdev->dev, "selftest cannot allocate chan resource\n"); err = -ENODEV; goto out; } tx = device->common.device_prep_dma_memcpy(dma_chan, IOAT_TEST_SIZE, 0); if (!tx) { dev_err(&device->pdev->dev, "Self-test prep failed, disabling\n"); err = -ENODEV; goto free_resources; } async_tx_ack(tx); addr = dma_map_single(dma_chan->device->dev, src, IOAT_TEST_SIZE, DMA_TO_DEVICE); tx->tx_set_src(addr, tx, 0); addr = dma_map_single(dma_chan->device->dev, dest, IOAT_TEST_SIZE, DMA_FROM_DEVICE); tx->tx_set_dest(addr, tx, 0); tx->callback = ioat_dma_test_callback; tx->callback_param = (void *)0x8086; cookie = tx->tx_submit(tx); if (cookie < 0) { dev_err(&device->pdev->dev, "Self-test setup failed, disabling\n"); err = -ENODEV; goto free_resources; } device->common.device_issue_pending(dma_chan); msleep(1); if (device->common.device_is_tx_complete(dma_chan, cookie, NULL, NULL) != DMA_SUCCESS) { dev_err(&device->pdev->dev, "Self-test copy timed out, disabling\n"); err = -ENODEV; goto free_resources; } if (memcmp(src, dest, IOAT_TEST_SIZE)) { dev_err(&device->pdev->dev, "Self-test copy failed compare, disabling\n"); err = -ENODEV; goto free_resources; }free_resources: device->common.device_free_chan_resources(dma_chan);out: kfree(src); kfree(dest); return err;}static char ioat_interrupt_style[32] = "msix";module_param_string(ioat_interrupt_style, ioat_interrupt_style, sizeof(ioat_interrupt_style), 0644);MODULE_PARM_DESC(ioat_interrupt_style, "set ioat interrupt style: msix (default), " "msix-single-vector, msi, intx)");/** * ioat_dma_setup_interrupts - setup interrupt handler * @device: ioat device */static int ioat_dma_setup_interrupts(struct ioatdma_device *device){ struct ioat_dma_chan *ioat_chan; int err, i, j, msixcnt; u8 intrctrl = 0; if (!strcmp(ioat_interrupt_style, "msix")) goto msix; if (!strcmp(ioat_interrupt_style, "msix-single-vector")) goto msix_single_vector; if (!strcmp(ioat_interrupt_style, "msi")) goto msi; if (!strcmp(ioat_interrupt_style, "intx")) goto intx; dev_err(&device->pdev->dev, "invalid ioat_interrupt_style %s\n", ioat_interrupt_style); goto err_no_irq;msix: /* The number of MSI-X vectors should equal the number of channels */ msixcnt = device->common.chancnt; for (i = 0; i < msixcnt; i++) device->msix_entries[i].entry = i; err = pci_enable_msix(device->pdev, device->msix_entries, msixcnt); if (err < 0) goto msi; if (err > 0) goto msix_single_vector; for (i = 0; i < msixcnt; i++) { ioat_chan = ioat_lookup_chan_by_index(device, i); err = request_irq(device->msix_entries[i].vector, ioat_dma_do_interrupt_msix, 0, "ioat-msix", ioat_chan); if (err) { for (j = 0; j < i; j++) { ioat_chan = ioat_lookup_chan_by_index(device, j); free_irq(device->msix_entries[j].vector, ioat_chan); } goto msix_single_vector; } } intrctrl |= IOAT_INTRCTRL_MSIX_VECTOR_CONTROL; device->irq_mode = msix_multi_vector; goto done;msix_single_vector: device->msix_entries[0].entry = 0; err = pci_enable_msix(device->pdev, device->msix_entries, 1); if (err) goto msi; err = request_irq(device->msix_entries[0].vector, ioat_dma_do_interrupt, 0, "ioat-msix", device); if (err) { pci_disable_msix(device->pdev); goto msi; } device->irq_mode = msix_single_vector; goto done;msi: err = pci_enable_msi(device->pdev); if (err) goto intx; err = request_irq(device->pdev->irq, ioat_dma_do_interrupt, 0, "ioat-msi", device); if (err) { pci_disable_msi(device->pdev); goto intx; } /* * CB 1.2 devices need a bit set in configuration space to enable MSI */ if (device->version == IOAT_VER_1_2) { u32 dmactrl; pci_read_config_dword(device->pdev, IOAT_PCI_DMACTRL_OFFSET, &dmactrl); dmactrl |= IOAT_PCI_DMACTRL_MSI_EN; pci_write_config_dword(device->pdev, IOAT_PCI_DMACTRL_OFFSET, dmactrl); } device->irq_mode = msi; goto done;intx: err = request_irq(device->pdev->irq, ioat_dma_do_interrupt, IRQF_SHARED, "ioat-intx", device); if (err) goto err_no_irq; device->irq_mode = intx;done: intrctrl |= IOAT_INTRCTRL_MASTER_INT_EN; writeb(intrctrl, device->reg_base + IOAT_INTRCTRL_OFFSET); return 0;err_no_irq: /* Disable all interrupt generation */ writeb(0, device->reg_base + IOAT_INTRCTRL_OFFSET); dev_err(&device->pdev->dev, "no usable interrupts\n"); device->irq_mode = none; return -1;}/** * ioat_dma_remove_interrupts - remove whatever interrupts were set * @device: ioat device */static void ioat_dma_remove_interrupts(struct ioatdma_device *device){ struct ioat_dma_chan *ioat_chan; int i; /* Disable all interrupt generation */ writeb(0, device->reg_base + IOAT_INTRCTRL_OFFSET); switch (device->irq_mode) { case msix_multi_vector: for (i = 0; i < device->common.chancnt; i++) { ioat_chan = ioat_lookup_chan_by_index(device, i); free_irq(device->msix_entries[i].vector, ioat_chan); } pci_disable_msix(device->pdev); break; case msix_single_vector: free_irq(device->msix_entries[0].vector, device); pci_disable_msix(device->pdev); break; case msi: free_irq(device->pdev->irq, device); pci_disable_msi(device->pdev); break; case intx: free_irq(device->pdev->irq, device); break; case none: dev_warn(&device->pdev->dev, "call to %s without interrupts setup\n", __func__); } device->irq_mode = none;}struct ioatdma_device *ioat_dma_probe(struct pci_dev *pdev, void __iomem *iobase){ int err; struct ioatdma_device *device; device = kzalloc(sizeof(*device), GFP_KERNEL); if (!device) { err = -ENOMEM; goto err_kzalloc; } device->pdev = pdev; device->reg_base = iobase; device->version = readb(device->reg_base + IOAT_VER_OFFSET); /* DMA coherent memory pool for DMA descriptor allocations */ device->dma_pool = pci_pool_create("dma_desc_pool", pdev, sizeof(struct ioat_dma_descriptor), 64, 0); if (!device->dma_pool) { err = -ENOMEM; goto err_dma_pool; } device->completion_pool = pci_pool_create("completion_pool", pdev, sizeof(u64), SMP_CACHE_BYTES, SMP_CACHE_BYTES); if (!device->completion_pool) { err = -ENOMEM; goto err_completion_pool; } INIT_LIST_HEAD(&device->common.channels); ioat_dma_enumerate_channels(device); device->common.device_alloc_chan_resources = ioat_dma_alloc_chan_resources; device->common.device_free_chan_resources = ioat_dma_free_chan_resources; device->common.dev = &pdev->dev; dma_cap_set(DMA_MEMCPY, device->common.cap_mask); device->common.device_is_tx_complete = ioat_dma_is_complete; device->common.device_dependency_added = ioat_dma_dependency_added; switch (device->version) { case IOAT_VER_1_2: device->common.device_prep_dma_memcpy = ioat1_dma_prep_memcpy; device->common.device_issue_pending = ioat1_dma_memcpy_issue_pending; break; case IOAT_VER_2_0: device->common.device_prep_dma_memcpy = ioat2_dma_prep_memcpy; device->common.device_issue_pending = ioat2_dma_memcpy_issue_pending; break; } dev_err(&device->pdev->dev, "Intel(R) I/OAT DMA Engine found," " %d channels, device version 0x%02x, driver version %s\n", device->common.chancnt, device->version, IOAT_DMA_VERSION); err = ioat_dma_setup_interrupts(device); if (err) goto err_setup_interrupts; err = ioat_dma_self_test(device); if (err) goto err_self_test; dma_async_device_register(&device->common); return device;err_self_test: ioat_dma_remove_interrupts(device);err_setup_interrupts: pci_pool_destroy(device->completion_pool);err_completion_pool: pci_pool_destroy(device->dma_pool);err_dma_pool: kfree(device);err_kzalloc: dev_err(&pdev->dev, "Intel(R) I/OAT DMA Engine initialization failed\n"); return NULL;}void ioat_dma_remove(struct ioatdma_device *device){ struct dma_chan *chan, *_chan; struct ioat_dma_chan *ioat_chan; ioat_dma_remove_interrupts(device); dma_async_device_unregister(&device->common); pci_pool_destroy(device->dma_pool); pci_pool_destroy(device->completion_pool); iounmap(device->reg_base); pci_release_regions(device->pdev); pci_disable_device(device->pdev); list_for_each_entry_safe(chan, _chan, &device->common.channels, device_node) { ioat_chan = to_ioat_chan(chan); list_del(&chan->device_node); kfree(ioat_chan); } kfree(device);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -