Merge tag 'dmaengine-6.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/dmaengine

Pull dmaengine updates from Vinod Koul:
 "New support:

   - Apple admac t8112 device support

   - StarFive JH7110 DMA controller

  Updates:

   - Big pile of idxd updates to support IAA 2.0 device capabilities,
     DSA 2.0 Event Log and completion record faulting features and
     new DSA operations

   - at_xdmac supend & resume updates and driver code cleanup

   - k3-udma supend & resume support

   - k3-psil thread support for J784s4"

* tag 'dmaengine-6.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/dmaengine: (57 commits)
  dmaengine: idxd: add per wq PRS disable
  dmaengine: idxd: add pid to exported sysfs attribute for opened file
  dmaengine: idxd: expose fault counters to sysfs
  dmaengine: idxd: add a device to represent the file opened
  dmaengine: idxd: add per file user counters for completion record faults
  dmaengine: idxd: process batch descriptor completion record faults
  dmaengine: idxd: add descs_completed field for completion record
  dmaengine: idxd: process user page faults for completion record
  dmaengine: idxd: add idxd_copy_cr() to copy user completion record during page fault handling
  dmaengine: idxd: create kmem cache for event log fault items
  dmaengine: idxd: add per DSA wq workqueue for processing cr faults
  dmanegine: idxd: add debugfs for event log dump
  dmaengine: idxd: add interrupt handling for event log
  dmaengine: idxd: setup event log configuration
  dmaengine: idxd: add event log size sysfs attribute
  dmaengine: idxd: make misc interrupt one shot
  dt-bindings: dma: snps,dw-axi-dmac: constrain the items of resets for JH7110 dma
  dt-bindings: dma: Drop unneeded quotes
  dmaengine: at_xdmac: align declaration of ret with the rest of variables
  dmaengine: at_xdmac: add a warning message regarding for unpaused channels
  ...
This commit is contained in:
Linus Torvalds
2023-05-03 11:11:56 -07:00
42 changed files with 1863 additions and 260 deletions

View File

@@ -136,6 +136,22 @@ Description: The last executed device administrative command's status/error.
Also last configuration error overloaded.
Writing to it will clear the status.
What: /sys/bus/dsa/devices/dsa<m>/iaa_cap
Date: Sept 14, 2022
KernelVersion: 6.0.0
Contact: dmaengine@vger.kernel.org
Description: IAA (IAX) capability mask. Exported to user space for application
consumption. This attribute should only be visible on IAA devices
that are version 2 or later.
What: /sys/bus/dsa/devices/dsa<m>/event_log_size
Date: Sept 14, 2022
KernelVersion: 6.4.0
Contact: dmaengine@vger.kernel.org
Description: The event log size to be configured. Default is 64 entries and
occupies 4k size if the evl entry is 64 bytes. It's visible
only on platforms that support the capability.
What: /sys/bus/dsa/devices/wq<m>.<n>/block_on_fault
Date: Oct 27, 2020
KernelVersion: 5.11.0
@@ -219,6 +235,16 @@ Contact: dmaengine@vger.kernel.org
Description: Indicate whether ATS disable is turned on for the workqueue.
0 indicates ATS is on, and 1 indicates ATS is off for the workqueue.
What: /sys/bus/dsa/devices/wq<m>.<n>/prs_disable
Date: Sept 14, 2022
KernelVersion: 6.4.0
Contact: dmaengine@vger.kernel.org
Description: Controls whether PRS disable is turned on for the workqueue.
0 indicates PRS is on, and 1 indicates PRS is off for the
workqueue. This option overrides block_on_fault attribute
if set. It's visible only on platforms that support the
capability.
What: /sys/bus/dsa/devices/wq<m>.<n>/occupancy
Date May 25, 2021
KernelVersion: 5.14.0
@@ -302,3 +328,28 @@ Description: Allows control of the number of batch descriptors that can be
1 (1/2 of max value), 2 (1/4 of the max value), and 3 (1/8 of
the max value). It's visible only on platforms that support
the capability.
What: /sys/bus/dsa/devices/wq<m>.<n>/dsa<x>\!wq<m>.<n>/file<y>/cr_faults
Date: Sept 14, 2022
KernelVersion: 6.4.0
Contact: dmaengine@vger.kernel.org
Description: Show the number of Completion Record (CR) faults this application
has caused.
What: /sys/bus/dsa/devices/wq<m>.<n>/dsa<x>\!wq<m>.<n>/file<y>/cr_fault_failures
Date: Sept 14, 2022
KernelVersion: 6.4.0
Contact: dmaengine@vger.kernel.org
Description: Show the number of Completion Record (CR) faults failures that this
application has caused. The failure counter is incremented when the
driver cannot fault in the address for the CR. Typically this is caused
by a bad address programmed in the submitted descriptor or a malicious
submitter is using bad CR address on purpose.
What: /sys/bus/dsa/devices/wq<m>.<n>/dsa<x>\!wq<m>.<n>/file<y>/pid
Date: Sept 14, 2022
KernelVersion: 6.4.0
Contact: dmaengine@vger.kernel.org
Description: Show the process id of the application that opened the file. This is
helpful information for a monitor daemon that wants to kill the
application that opened the file.

View File

@@ -26,6 +26,7 @@ properties:
- enum:
- apple,t6000-admac
- apple,t8103-admac
- apple,t8112-admac
- const: apple,admac
reg:

View File

@@ -24,6 +24,7 @@ properties:
- qcom,sm6350-gpi-dma
- items:
- enum:
- qcom,qcm2290-gpi-dma
- qcom,qdu1000-gpi-dma
- qcom,sc7280-gpi-dma
- qcom,sm6115-gpi-dma

View File

@@ -54,6 +54,11 @@ properties:
- description: DMA main clock
- description: DMA register access clock
clock-names:
items:
- const: main
- const: register
'#dma-cells':
const: 1
description:
@@ -77,16 +82,23 @@ properties:
- description: Reset for DMA ARESETN reset terminal
- description: Reset for DMA RST_ASYNC reset terminal
reset-names:
items:
- const: arst
- const: rst_async
required:
- compatible
- reg
- interrupts
- interrupt-names
- clocks
- clock-names
- '#dma-cells'
- dma-channels
- power-domains
- resets
- reset-names
additionalProperties: false
@@ -124,9 +136,11 @@ examples:
"ch12", "ch13", "ch14", "ch15";
clocks = <&cpg CPG_MOD R9A07G044_DMAC_ACLK>,
<&cpg CPG_MOD R9A07G044_DMAC_PCLK>;
clock-names = "main", "register";
power-domains = <&cpg>;
resets = <&cpg R9A07G044_DMAC_ARESETN>,
<&cpg R9A07G044_DMAC_RST_ASYNC>;
reset-names = "arst", "rst_async";
#dma-cells = <1>;
dma-channels = <16>;
};

View File

@@ -20,6 +20,7 @@ properties:
enum:
- snps,axi-dma-1.01a
- intel,kmb-axi-dma
- starfive,jh7110-axi-dma
reg:
minItems: 1
@@ -58,7 +59,8 @@ properties:
maximum: 8
resets:
maxItems: 1
minItems: 1
maxItems: 2
snps,dma-masters:
description: |
@@ -109,6 +111,25 @@ required:
- snps,priority
- snps,block-size
if:
properties:
compatible:
contains:
enum:
- starfive,jh7110-axi-dma
then:
properties:
resets:
minItems: 2
items:
- description: AXI reset line
- description: AHB reset line
- description: module reset
else:
properties:
resets:
maxItems: 1
additionalProperties: false
examples:

View File

@@ -43,7 +43,7 @@ description: |
configuration of the legacy peripheral.
allOf:
- $ref: "../dma-controller.yaml#"
- $ref: ../dma-controller.yaml#
- $ref: /schemas/arm/keystone/ti,k3-sci-common.yaml#
properties:

View File

@@ -15,7 +15,7 @@ maintainers:
- Michael Tretter <m.tretter@pengutronix.de>
allOf:
- $ref: "../dma-controller.yaml#"
- $ref: ../dma-controller.yaml#
properties:
"#dma-cells":

View File

@@ -16,7 +16,7 @@ maintainers:
- Laurent Pinchart <laurent.pinchart@ideasonboard.com>
allOf:
- $ref: "../dma-controller.yaml#"
- $ref: ../dma-controller.yaml#
properties:
"#dma-cells":

View File

@@ -623,6 +623,7 @@ config TEGRA186_GPC_DMA
depends on (ARCH_TEGRA || COMPILE_TEST) && ARCH_DMA_ADDR_T_64BIT
depends on IOMMU_API
select DMA_ENGINE
select DMA_VIRTUAL_CHANNELS
help
Support for the NVIDIA Tegra General Purpose Central DMA controller.
The DMA controller has multiple DMA channels which can be configured

View File

@@ -187,6 +187,7 @@
enum atc_status {
AT_XDMAC_CHAN_IS_CYCLIC = 0,
AT_XDMAC_CHAN_IS_PAUSED,
AT_XDMAC_CHAN_IS_PAUSED_INTERNAL,
};
struct at_xdmac_layout {
@@ -245,6 +246,7 @@ struct at_xdmac {
int irq;
struct clk *clk;
u32 save_gim;
u32 save_gs;
struct dma_pool *at_xdmac_desc_pool;
const struct at_xdmac_layout *layout;
struct at_xdmac_chan chan[];
@@ -347,6 +349,11 @@ static inline int at_xdmac_chan_is_paused(struct at_xdmac_chan *atchan)
return test_bit(AT_XDMAC_CHAN_IS_PAUSED, &atchan->status);
}
static inline int at_xdmac_chan_is_paused_internal(struct at_xdmac_chan *atchan)
{
return test_bit(AT_XDMAC_CHAN_IS_PAUSED_INTERNAL, &atchan->status);
}
static inline bool at_xdmac_chan_is_peripheral_xfer(u32 cfg)
{
return cfg & AT_XDMAC_CC_TYPE_PER_TRAN;
@@ -412,7 +419,7 @@ static bool at_xdmac_chan_is_enabled(struct at_xdmac_chan *atchan)
return ret;
}
static void at_xdmac_off(struct at_xdmac *atxdmac)
static void at_xdmac_off(struct at_xdmac *atxdmac, bool suspend_descriptors)
{
struct dma_chan *chan, *_chan;
struct at_xdmac_chan *atchan;
@@ -431,7 +438,7 @@ static void at_xdmac_off(struct at_xdmac *atxdmac)
at_xdmac_write(atxdmac, AT_XDMAC_GID, -1L);
/* Decrement runtime PM ref counter for each active descriptor. */
if (!list_empty(&atxdmac->dma.channels)) {
if (!list_empty(&atxdmac->dma.channels) && suspend_descriptors) {
list_for_each_entry_safe(chan, _chan, &atxdmac->dma.channels,
device_node) {
atchan = to_at_xdmac_chan(chan);
@@ -1898,6 +1905,26 @@ static int at_xdmac_device_config(struct dma_chan *chan,
return ret;
}
static void at_xdmac_device_pause_set(struct at_xdmac *atxdmac,
struct at_xdmac_chan *atchan)
{
at_xdmac_write(atxdmac, atxdmac->layout->grws, atchan->mask);
while (at_xdmac_chan_read(atchan, AT_XDMAC_CC) &
(AT_XDMAC_CC_WRIP | AT_XDMAC_CC_RDIP))
cpu_relax();
}
static void at_xdmac_device_pause_internal(struct at_xdmac_chan *atchan)
{
struct at_xdmac *atxdmac = to_at_xdmac(atchan->chan.device);
unsigned long flags;
spin_lock_irqsave(&atchan->lock, flags);
set_bit(AT_XDMAC_CHAN_IS_PAUSED_INTERNAL, &atchan->status);
at_xdmac_device_pause_set(atxdmac, atchan);
spin_unlock_irqrestore(&atchan->lock, flags);
}
static int at_xdmac_device_pause(struct dma_chan *chan)
{
struct at_xdmac_chan *atchan = to_at_xdmac_chan(chan);
@@ -1915,11 +1942,8 @@ static int at_xdmac_device_pause(struct dma_chan *chan)
return ret;
spin_lock_irqsave(&atchan->lock, flags);
at_xdmac_write(atxdmac, atxdmac->layout->grws, atchan->mask);
while (at_xdmac_chan_read(atchan, AT_XDMAC_CC)
& (AT_XDMAC_CC_WRIP | AT_XDMAC_CC_RDIP))
cpu_relax();
at_xdmac_device_pause_set(atxdmac, atchan);
/* Decrement runtime PM ref counter for each active descriptor. */
at_xdmac_runtime_suspend_descriptors(atchan);
@@ -1931,6 +1955,17 @@ static int at_xdmac_device_pause(struct dma_chan *chan)
return 0;
}
static void at_xdmac_device_resume_internal(struct at_xdmac_chan *atchan)
{
struct at_xdmac *atxdmac = to_at_xdmac(atchan->chan.device);
unsigned long flags;
spin_lock_irqsave(&atchan->lock, flags);
at_xdmac_write(atxdmac, atxdmac->layout->grwr, atchan->mask);
clear_bit(AT_XDMAC_CHAN_IS_PAUSED_INTERNAL, &atchan->status);
spin_unlock_irqrestore(&atchan->lock, flags);
}
static int at_xdmac_device_resume(struct dma_chan *chan)
{
struct at_xdmac_chan *atchan = to_at_xdmac_chan(chan);
@@ -2118,19 +2153,26 @@ static int __maybe_unused atmel_xdmac_suspend(struct device *dev)
atchan->save_cc = at_xdmac_chan_read(atchan, AT_XDMAC_CC);
if (at_xdmac_chan_is_cyclic(atchan)) {
if (!at_xdmac_chan_is_paused(atchan))
at_xdmac_device_pause(chan);
if (!at_xdmac_chan_is_paused(atchan)) {
dev_warn(chan2dev(chan), "%s: channel %d not paused\n",
__func__, chan->chan_id);
at_xdmac_device_pause_internal(atchan);
at_xdmac_runtime_suspend_descriptors(atchan);
}
atchan->save_cim = at_xdmac_chan_read(atchan, AT_XDMAC_CIM);
atchan->save_cnda = at_xdmac_chan_read(atchan, AT_XDMAC_CNDA);
atchan->save_cndc = at_xdmac_chan_read(atchan, AT_XDMAC_CNDC);
}
at_xdmac_runtime_suspend_descriptors(atchan);
}
atxdmac->save_gim = at_xdmac_read(atxdmac, AT_XDMAC_GIM);
atxdmac->save_gs = at_xdmac_read(atxdmac, AT_XDMAC_GS);
at_xdmac_off(atxdmac);
return pm_runtime_force_suspend(atxdmac->dev);
at_xdmac_off(atxdmac, false);
pm_runtime_mark_last_busy(atxdmac->dev);
pm_runtime_put_noidle(atxdmac->dev);
clk_disable_unprepare(atxdmac->clk);
return 0;
}
static int __maybe_unused atmel_xdmac_resume(struct device *dev)
@@ -2139,13 +2181,14 @@ static int __maybe_unused atmel_xdmac_resume(struct device *dev)
struct at_xdmac_chan *atchan;
struct dma_chan *chan, *_chan;
struct platform_device *pdev = container_of(dev, struct platform_device, dev);
int i;
int ret;
int i, ret;
ret = pm_runtime_force_resume(atxdmac->dev);
if (ret < 0)
ret = clk_prepare_enable(atxdmac->clk);
if (ret)
return ret;
pm_runtime_get_noresume(atxdmac->dev);
at_xdmac_axi_config(pdev);
/* Clear pending interrupts. */
@@ -2159,19 +2202,33 @@ static int __maybe_unused atmel_xdmac_resume(struct device *dev)
list_for_each_entry_safe(chan, _chan, &atxdmac->dma.channels, device_node) {
atchan = to_at_xdmac_chan(chan);
ret = at_xdmac_runtime_resume_descriptors(atchan);
if (ret < 0)
return ret;
at_xdmac_chan_write(atchan, AT_XDMAC_CC, atchan->save_cc);
if (at_xdmac_chan_is_cyclic(atchan)) {
if (at_xdmac_chan_is_paused(atchan))
at_xdmac_device_resume(chan);
/*
* Resume only channels not explicitly paused by
* consumers.
*/
if (at_xdmac_chan_is_paused_internal(atchan)) {
ret = at_xdmac_runtime_resume_descriptors(atchan);
if (ret < 0)
return ret;
at_xdmac_device_resume_internal(atchan);
}
/*
* We may resume from a deep sleep state where power
* to DMA controller is cut-off. Thus, restore the
* suspend state of channels set though dmaengine API.
*/
else if (at_xdmac_chan_is_paused(atchan))
at_xdmac_device_pause_set(atxdmac, atchan);
at_xdmac_chan_write(atchan, AT_XDMAC_CNDA, atchan->save_cnda);
at_xdmac_chan_write(atchan, AT_XDMAC_CNDC, atchan->save_cndc);
at_xdmac_chan_write(atchan, AT_XDMAC_CIE, atchan->save_cim);
wmb();
at_xdmac_write(atxdmac, AT_XDMAC_GE, atchan->mask);
if (atxdmac->save_gs & atchan->mask)
at_xdmac_write(atxdmac, AT_XDMAC_GE, atchan->mask);
}
}
@@ -2312,7 +2369,7 @@ static int at_xdmac_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&atxdmac->dma.channels);
/* Disable all chans and interrupts. */
at_xdmac_off(atxdmac);
at_xdmac_off(atxdmac, true);
for (i = 0; i < nr_channels; i++) {
struct at_xdmac_chan *atchan = &atxdmac->chan[i];
@@ -2376,7 +2433,7 @@ static int at_xdmac_remove(struct platform_device *pdev)
struct at_xdmac *atxdmac = (struct at_xdmac *)platform_get_drvdata(pdev);
int i;
at_xdmac_off(atxdmac);
at_xdmac_off(atxdmac, true);
of_dma_controller_free(pdev->dev.of_node);
dma_async_device_unregister(&atxdmac->dma);
pm_runtime_disable(atxdmac->dev);

View File

@@ -38,7 +38,7 @@ int bcom_sram_init(struct device_node *sram_node, char *owner)
{
int rv;
const u32 *regaddr_p;
u64 regaddr64, size64;
struct resource res;
unsigned int psize;
/* Create our state struct */
@@ -56,21 +56,18 @@ int bcom_sram_init(struct device_node *sram_node, char *owner)
}
/* Get address and size of the sram */
regaddr_p = of_get_address(sram_node, 0, &size64, NULL);
if (!regaddr_p) {
rv = of_address_to_resource(sram_node, 0, &res);
if (rv) {
printk(KERN_ERR "%s: bcom_sram_init: "
"Invalid device node !\n", owner);
rv = -EINVAL;
goto error_free;
}
regaddr64 = of_translate_address(sram_node, regaddr_p);
bcom_sram->base_phys = (phys_addr_t) regaddr64;
bcom_sram->size = (unsigned int) size64;
bcom_sram->base_phys = res.start;
bcom_sram->size = resource_size(&res);
/* Request region */
if (!request_mem_region(bcom_sram->base_phys, bcom_sram->size, owner)) {
if (!request_mem_region(res.start, resource_size(&res), owner)) {
printk(KERN_ERR "%s: bcom_sram_init: "
"Couldn't request region !\n", owner);
rv = -EBUSY;
@@ -79,7 +76,7 @@ int bcom_sram_init(struct device_node *sram_node, char *owner)
/* Map SRAM */
/* sram is not really __iomem */
bcom_sram->base_virt = (void*) ioremap(bcom_sram->base_phys, bcom_sram->size);
bcom_sram->base_virt = (void *)ioremap(res.start, resource_size(&res));
if (!bcom_sram->base_virt) {
printk(KERN_ERR "%s: bcom_sram_init: "
@@ -120,7 +117,7 @@ int bcom_sram_init(struct device_node *sram_node, char *owner)
return 0;
error_release:
release_mem_region(bcom_sram->base_phys, bcom_sram->size);
release_mem_region(res.start, resource_size(&res));
error_free:
kfree(bcom_sram);
bcom_sram = NULL;

View File

@@ -21,10 +21,12 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_dma.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/property.h>
#include <linux/reset.h>
#include <linux/slab.h>
#include <linux/types.h>
@@ -46,6 +48,10 @@
DMA_SLAVE_BUSWIDTH_32_BYTES | \
DMA_SLAVE_BUSWIDTH_64_BYTES)
#define AXI_DMA_FLAG_HAS_APB_REGS BIT(0)
#define AXI_DMA_FLAG_HAS_RESETS BIT(1)
#define AXI_DMA_FLAG_USE_CFG2 BIT(2)
static inline void
axi_dma_iowrite32(struct axi_dma_chip *chip, u32 reg, u32 val)
{
@@ -86,7 +92,8 @@ static inline void axi_chan_config_write(struct axi_dma_chan *chan,
cfg_lo = (config->dst_multblk_type << CH_CFG_L_DST_MULTBLK_TYPE_POS |
config->src_multblk_type << CH_CFG_L_SRC_MULTBLK_TYPE_POS);
if (chan->chip->dw->hdata->reg_map_8_channels) {
if (chan->chip->dw->hdata->reg_map_8_channels &&
!chan->chip->dw->hdata->use_cfg2) {
cfg_hi = config->tt_fc << CH_CFG_H_TT_FC_POS |
config->hs_sel_src << CH_CFG_H_HS_SEL_SRC_POS |
config->hs_sel_dst << CH_CFG_H_HS_SEL_DST_POS |
@@ -1140,7 +1147,7 @@ static int dma_chan_terminate_all(struct dma_chan *dchan)
axi_chan_disable(chan);
ret = readl_poll_timeout_atomic(chan->chip->regs + DMAC_CHEN, val,
!(val & chan_active), 1000, 10000);
!(val & chan_active), 1000, 50000);
if (ret == -ETIMEDOUT)
dev_warn(dchan2dev(dchan),
"%s failed to stop\n", axi_chan_name(chan));
@@ -1367,10 +1374,11 @@ static int parse_device_properties(struct axi_dma_chip *chip)
static int dw_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
struct axi_dma_chip *chip;
struct dw_axi_dma *dw;
struct dw_axi_dma_hcfg *hdata;
struct reset_control *resets;
unsigned int flags;
u32 i;
int ret;
@@ -1398,12 +1406,25 @@ static int dw_probe(struct platform_device *pdev)
if (IS_ERR(chip->regs))
return PTR_ERR(chip->regs);
if (of_device_is_compatible(node, "intel,kmb-axi-dma")) {
flags = (uintptr_t)of_device_get_match_data(&pdev->dev);
if (flags & AXI_DMA_FLAG_HAS_APB_REGS) {
chip->apb_regs = devm_platform_ioremap_resource(pdev, 1);
if (IS_ERR(chip->apb_regs))
return PTR_ERR(chip->apb_regs);
}
if (flags & AXI_DMA_FLAG_HAS_RESETS) {
resets = devm_reset_control_array_get_exclusive(&pdev->dev);
if (IS_ERR(resets))
return PTR_ERR(resets);
ret = reset_control_deassert(resets);
if (ret)
return ret;
}
chip->dw->hdata->use_cfg2 = !!(flags & AXI_DMA_FLAG_USE_CFG2);
chip->core_clk = devm_clk_get(chip->dev, "core-clk");
if (IS_ERR(chip->core_clk))
return PTR_ERR(chip->core_clk);
@@ -1554,8 +1575,15 @@ static const struct dev_pm_ops dw_axi_dma_pm_ops = {
};
static const struct of_device_id dw_dma_of_id_table[] = {
{ .compatible = "snps,axi-dma-1.01a" },
{ .compatible = "intel,kmb-axi-dma" },
{
.compatible = "snps,axi-dma-1.01a"
}, {
.compatible = "intel,kmb-axi-dma",
.data = (void *)AXI_DMA_FLAG_HAS_APB_REGS,
}, {
.compatible = "starfive,jh7110-axi-dma",
.data = (void *)(AXI_DMA_FLAG_HAS_RESETS | AXI_DMA_FLAG_USE_CFG2),
},
{}
};
MODULE_DEVICE_TABLE(of, dw_dma_of_id_table);

View File

@@ -33,6 +33,7 @@ struct dw_axi_dma_hcfg {
/* Register map for DMAX_NUM_CHANNELS <= 8 */
bool reg_map_8_channels;
bool restrict_axi_burst_len;
bool use_cfg2;
};
struct axi_dma_chan {

View File

@@ -181,7 +181,7 @@ static void vchan_free_desc(struct virt_dma_desc *vdesc)
dw_edma_free_desc(vd2dw_edma_desc(vdesc));
}
static void dw_edma_start_transfer(struct dw_edma_chan *chan)
static int dw_edma_start_transfer(struct dw_edma_chan *chan)
{
struct dw_edma_chunk *child;
struct dw_edma_desc *desc;
@@ -189,16 +189,16 @@ static void dw_edma_start_transfer(struct dw_edma_chan *chan)
vd = vchan_next_desc(&chan->vc);
if (!vd)
return;
return 0;
desc = vd2dw_edma_desc(vd);
if (!desc)
return;
return 0;
child = list_first_entry_or_null(&desc->chunk->list,
struct dw_edma_chunk, list);
if (!child)
return;
return 0;
dw_edma_v0_core_start(child, !desc->xfer_sz);
desc->xfer_sz += child->ll_region.sz;
@@ -206,6 +206,8 @@ static void dw_edma_start_transfer(struct dw_edma_chan *chan)
list_del(&child->list);
kfree(child);
desc->chunks_alloc--;
return 1;
}
static void dw_edma_device_caps(struct dma_chan *dchan,
@@ -306,9 +308,12 @@ static void dw_edma_device_issue_pending(struct dma_chan *dchan)
struct dw_edma_chan *chan = dchan2dw_edma_chan(dchan);
unsigned long flags;
if (!chan->configured)
return;
spin_lock_irqsave(&chan->vc.lock, flags);
if (chan->configured && chan->request == EDMA_REQ_NONE &&
chan->status == EDMA_ST_IDLE && vchan_issue_pending(&chan->vc)) {
if (vchan_issue_pending(&chan->vc) && chan->request == EDMA_REQ_NONE &&
chan->status == EDMA_ST_IDLE) {
chan->status = EDMA_ST_BUSY;
dw_edma_start_transfer(chan);
}
@@ -602,14 +607,14 @@ static void dw_edma_done_interrupt(struct dw_edma_chan *chan)
switch (chan->request) {
case EDMA_REQ_NONE:
desc = vd2dw_edma_desc(vd);
if (desc->chunks_alloc) {
chan->status = EDMA_ST_BUSY;
dw_edma_start_transfer(chan);
} else {
if (!desc->chunks_alloc) {
list_del(&vd->node);
vchan_cookie_complete(vd);
chan->status = EDMA_ST_IDLE;
}
/* Continue transferring if there are remaining chunks or issued requests.
*/
chan->status = dw_edma_start_transfer(chan) ? EDMA_ST_BUSY : EDMA_ST_IDLE;
break;
case EDMA_REQ_STOP:

View File

@@ -159,62 +159,6 @@ static inline u32 readl_ch(struct dw_edma *dw, enum dw_edma_dir dir, u16 ch,
#define GET_CH_32(dw, dir, ch, name) \
readl_ch(dw, dir, ch, &(__dw_ch_regs(dw, dir, ch)->name))
static inline void writeq_ch(struct dw_edma *dw, enum dw_edma_dir dir, u16 ch,
u64 value, void __iomem *addr)
{
if (dw->chip->mf == EDMA_MF_EDMA_LEGACY) {
u32 viewport_sel;
unsigned long flags;
raw_spin_lock_irqsave(&dw->lock, flags);
viewport_sel = FIELD_PREP(EDMA_V0_VIEWPORT_MASK, ch);
if (dir == EDMA_DIR_READ)
viewport_sel |= BIT(31);
writel(viewport_sel,
&(__dw_regs(dw)->type.legacy.viewport_sel));
writeq(value, addr);
raw_spin_unlock_irqrestore(&dw->lock, flags);
} else {
writeq(value, addr);
}
}
static inline u64 readq_ch(struct dw_edma *dw, enum dw_edma_dir dir, u16 ch,
const void __iomem *addr)
{
u64 value;
if (dw->chip->mf == EDMA_MF_EDMA_LEGACY) {
u32 viewport_sel;
unsigned long flags;
raw_spin_lock_irqsave(&dw->lock, flags);
viewport_sel = FIELD_PREP(EDMA_V0_VIEWPORT_MASK, ch);
if (dir == EDMA_DIR_READ)
viewport_sel |= BIT(31);
writel(viewport_sel,
&(__dw_regs(dw)->type.legacy.viewport_sel));
value = readq(addr);
raw_spin_unlock_irqrestore(&dw->lock, flags);
} else {
value = readq(addr);
}
return value;
}
#define SET_CH_64(dw, dir, ch, name, value) \
writeq_ch(dw, dir, ch, value, &(__dw_ch_regs(dw, dir, ch)->name))
#define GET_CH_64(dw, dir, ch, name) \
readq_ch(dw, dir, ch, &(__dw_ch_regs(dw, dir, ch)->name))
/* eDMA management callbacks */
void dw_edma_v0_core_off(struct dw_edma *dw)
{

View File

@@ -1,7 +1,7 @@
ccflags-y += -DDEFAULT_SYMBOL_NAMESPACE=IDXD
obj-$(CONFIG_INTEL_IDXD) += idxd.o
idxd-y := init.o irq.o device.o sysfs.o submit.o dma.o cdev.o
idxd-y := init.o irq.o device.o sysfs.o submit.o dma.o cdev.o debugfs.o
idxd-$(CONFIG_INTEL_IDXD_PERFMON) += perfmon.o

View File

@@ -11,7 +11,9 @@
#include <linux/fs.h>
#include <linux/poll.h>
#include <linux/iommu.h>
#include <linux/highmem.h>
#include <uapi/linux/idxd.h>
#include <linux/xarray.h>
#include "registers.h"
#include "idxd.h"
@@ -21,6 +23,13 @@ struct idxd_cdev_context {
struct ida minor_ida;
};
/*
* Since user file names are global in DSA devices, define their ida's as
* global to avoid conflict file names.
*/
static DEFINE_IDA(file_ida);
static DEFINE_MUTEX(ida_lock);
/*
* ictx is an array based off of accelerator types. enum idxd_type
* is used as index
@@ -34,8 +43,119 @@ struct idxd_user_context {
struct idxd_wq *wq;
struct task_struct *task;
unsigned int pasid;
struct mm_struct *mm;
unsigned int flags;
struct iommu_sva *sva;
struct idxd_dev idxd_dev;
u64 counters[COUNTER_MAX];
int id;
pid_t pid;
};
static void idxd_cdev_evl_drain_pasid(struct idxd_wq *wq, u32 pasid);
static void idxd_xa_pasid_remove(struct idxd_user_context *ctx);
static inline struct idxd_user_context *dev_to_uctx(struct device *dev)
{
struct idxd_dev *idxd_dev = confdev_to_idxd_dev(dev);
return container_of(idxd_dev, struct idxd_user_context, idxd_dev);
}
static ssize_t cr_faults_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct idxd_user_context *ctx = dev_to_uctx(dev);
return sysfs_emit(buf, "%llu\n", ctx->counters[COUNTER_FAULTS]);
}
static DEVICE_ATTR_RO(cr_faults);
static ssize_t cr_fault_failures_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct idxd_user_context *ctx = dev_to_uctx(dev);
return sysfs_emit(buf, "%llu\n", ctx->counters[COUNTER_FAULT_FAILS]);
}
static DEVICE_ATTR_RO(cr_fault_failures);
static ssize_t pid_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct idxd_user_context *ctx = dev_to_uctx(dev);
return sysfs_emit(buf, "%u\n", ctx->pid);
}
static DEVICE_ATTR_RO(pid);
static struct attribute *cdev_file_attributes[] = {
&dev_attr_cr_faults.attr,
&dev_attr_cr_fault_failures.attr,
&dev_attr_pid.attr,
NULL
};
static umode_t cdev_file_attr_visible(struct kobject *kobj, struct attribute *a, int n)
{
struct device *dev = container_of(kobj, typeof(*dev), kobj);
struct idxd_user_context *ctx = dev_to_uctx(dev);
struct idxd_wq *wq = ctx->wq;
if (!wq_pasid_enabled(wq))
return 0;
return a->mode;
}
static const struct attribute_group cdev_file_attribute_group = {
.attrs = cdev_file_attributes,
.is_visible = cdev_file_attr_visible,
};
static const struct attribute_group *cdev_file_attribute_groups[] = {
&cdev_file_attribute_group,
NULL
};
static void idxd_file_dev_release(struct device *dev)
{
struct idxd_user_context *ctx = dev_to_uctx(dev);
struct idxd_wq *wq = ctx->wq;
struct idxd_device *idxd = wq->idxd;
int rc;
mutex_lock(&ida_lock);
ida_free(&file_ida, ctx->id);
mutex_unlock(&ida_lock);
/* Wait for in-flight operations to complete. */
if (wq_shared(wq)) {
idxd_device_drain_pasid(idxd, ctx->pasid);
} else {
if (device_user_pasid_enabled(idxd)) {
/* The wq disable in the disable pasid function will drain the wq */
rc = idxd_wq_disable_pasid(wq);
if (rc < 0)
dev_err(dev, "wq disable pasid failed.\n");
} else {
idxd_wq_drain(wq);
}
}
if (ctx->sva) {
idxd_cdev_evl_drain_pasid(wq, ctx->pasid);
iommu_sva_unbind_device(ctx->sva);
idxd_xa_pasid_remove(ctx);
}
kfree(ctx);
mutex_lock(&wq->wq_lock);
idxd_wq_put(wq);
mutex_unlock(&wq->wq_lock);
}
static struct device_type idxd_cdev_file_type = {
.name = "idxd_file",
.release = idxd_file_dev_release,
.groups = cdev_file_attribute_groups,
};
static void idxd_cdev_dev_release(struct device *dev)
@@ -68,15 +188,46 @@ static inline struct idxd_wq *inode_wq(struct inode *inode)
return idxd_cdev->wq;
}
static void idxd_xa_pasid_remove(struct idxd_user_context *ctx)
{
struct idxd_wq *wq = ctx->wq;
void *ptr;
mutex_lock(&wq->uc_lock);
ptr = xa_cmpxchg(&wq->upasid_xa, ctx->pasid, ctx, NULL, GFP_KERNEL);
if (ptr != (void *)ctx)
dev_warn(&wq->idxd->pdev->dev, "xarray cmpxchg failed for pasid %u\n",
ctx->pasid);
mutex_unlock(&wq->uc_lock);
}
void idxd_user_counter_increment(struct idxd_wq *wq, u32 pasid, int index)
{
struct idxd_user_context *ctx;
if (index >= COUNTER_MAX)
return;
mutex_lock(&wq->uc_lock);
ctx = xa_load(&wq->upasid_xa, pasid);
if (!ctx) {
mutex_unlock(&wq->uc_lock);
return;
}
ctx->counters[index]++;
mutex_unlock(&wq->uc_lock);
}
static int idxd_cdev_open(struct inode *inode, struct file *filp)
{
struct idxd_user_context *ctx;
struct idxd_device *idxd;
struct idxd_wq *wq;
struct device *dev;
struct device *dev, *fdev;
int rc = 0;
struct iommu_sva *sva;
unsigned int pasid;
struct idxd_cdev *idxd_cdev;
wq = inode_wq(inode);
idxd = wq->idxd;
@@ -97,6 +248,7 @@ static int idxd_cdev_open(struct inode *inode, struct file *filp)
ctx->wq = wq;
filp->private_data = ctx;
ctx->pid = current->pid;
if (device_user_pasid_enabled(idxd)) {
sva = iommu_sva_bind_device(dev, current->mm);
@@ -108,65 +260,118 @@ static int idxd_cdev_open(struct inode *inode, struct file *filp)
pasid = iommu_sva_get_pasid(sva);
if (pasid == IOMMU_PASID_INVALID) {
iommu_sva_unbind_device(sva);
rc = -EINVAL;
goto failed;
goto failed_get_pasid;
}
ctx->sva = sva;
ctx->pasid = pasid;
ctx->mm = current->mm;
mutex_lock(&wq->uc_lock);
rc = xa_insert(&wq->upasid_xa, pasid, ctx, GFP_KERNEL);
mutex_unlock(&wq->uc_lock);
if (rc < 0)
dev_warn(dev, "PASID entry already exist in xarray.\n");
if (wq_dedicated(wq)) {
rc = idxd_wq_set_pasid(wq, pasid);
if (rc < 0) {
iommu_sva_unbind_device(sva);
dev_err(dev, "wq set pasid failed: %d\n", rc);
goto failed;
goto failed_set_pasid;
}
}
}
idxd_cdev = wq->idxd_cdev;
mutex_lock(&ida_lock);
ctx->id = ida_alloc(&file_ida, GFP_KERNEL);
mutex_unlock(&ida_lock);
if (ctx->id < 0) {
dev_warn(dev, "ida alloc failure\n");
goto failed_ida;
}
ctx->idxd_dev.type = IDXD_DEV_CDEV_FILE;
fdev = user_ctx_dev(ctx);
device_initialize(fdev);
fdev->parent = cdev_dev(idxd_cdev);
fdev->bus = &dsa_bus_type;
fdev->type = &idxd_cdev_file_type;
rc = dev_set_name(fdev, "file%d", ctx->id);
if (rc < 0) {
dev_warn(dev, "set name failure\n");
goto failed_dev_name;
}
rc = device_add(fdev);
if (rc < 0) {
dev_warn(dev, "file device add failure\n");
goto failed_dev_add;
}
idxd_wq_get(wq);
mutex_unlock(&wq->wq_lock);
return 0;
failed:
failed_dev_add:
failed_dev_name:
put_device(fdev);
failed_ida:
failed_set_pasid:
if (device_user_pasid_enabled(idxd))
idxd_xa_pasid_remove(ctx);
failed_get_pasid:
if (device_user_pasid_enabled(idxd))
iommu_sva_unbind_device(sva);
failed:
mutex_unlock(&wq->wq_lock);
kfree(ctx);
return rc;
}
static void idxd_cdev_evl_drain_pasid(struct idxd_wq *wq, u32 pasid)
{
struct idxd_device *idxd = wq->idxd;
struct idxd_evl *evl = idxd->evl;
union evl_status_reg status;
u16 h, t, size;
int ent_size = evl_ent_size(idxd);
struct __evl_entry *entry_head;
if (!evl)
return;
spin_lock(&evl->lock);
status.bits = ioread64(idxd->reg_base + IDXD_EVLSTATUS_OFFSET);
t = status.tail;
h = evl->head;
size = evl->size;
while (h != t) {
entry_head = (struct __evl_entry *)(evl->log + (h * ent_size));
if (entry_head->pasid == pasid && entry_head->wq_idx == wq->id)
set_bit(h, evl->bmap);
h = (h + 1) % size;
}
spin_unlock(&evl->lock);
drain_workqueue(wq->wq);
}
static int idxd_cdev_release(struct inode *node, struct file *filep)
{
struct idxd_user_context *ctx = filep->private_data;
struct idxd_wq *wq = ctx->wq;
struct idxd_device *idxd = wq->idxd;
struct device *dev = &idxd->pdev->dev;
int rc;
dev_dbg(dev, "%s called\n", __func__);
filep->private_data = NULL;
/* Wait for in-flight operations to complete. */
if (wq_shared(wq)) {
idxd_device_drain_pasid(idxd, ctx->pasid);
} else {
if (device_user_pasid_enabled(idxd)) {
/* The wq disable in the disable pasid function will drain the wq */
rc = idxd_wq_disable_pasid(wq);
if (rc < 0)
dev_err(dev, "wq disable pasid failed.\n");
} else {
idxd_wq_drain(wq);
}
}
device_unregister(user_ctx_dev(ctx));
if (ctx->sva)
iommu_sva_unbind_device(ctx->sva);
kfree(ctx);
mutex_lock(&wq->wq_lock);
idxd_wq_put(wq);
mutex_unlock(&wq->wq_lock);
return 0;
}
@@ -297,6 +502,7 @@ void idxd_wq_del_cdev(struct idxd_wq *wq)
struct idxd_cdev *idxd_cdev;
idxd_cdev = wq->idxd_cdev;
ida_destroy(&file_ida);
wq->idxd_cdev = NULL;
cdev_device_del(&idxd_cdev->cdev, cdev_dev(idxd_cdev));
put_device(cdev_dev(idxd_cdev));
@@ -330,6 +536,13 @@ static int idxd_user_drv_probe(struct idxd_dev *idxd_dev)
}
mutex_lock(&wq->wq_lock);
wq->wq = create_workqueue(dev_name(wq_confdev(wq)));
if (!wq->wq) {
rc = -ENOMEM;
goto wq_err;
}
wq->type = IDXD_WQT_USER;
rc = drv_enable_wq(wq);
if (rc < 0)
@@ -348,7 +561,9 @@ static int idxd_user_drv_probe(struct idxd_dev *idxd_dev)
err_cdev:
drv_disable_wq(wq);
err:
destroy_workqueue(wq->wq);
wq->type = IDXD_WQT_NONE;
wq_err:
mutex_unlock(&wq->wq_lock);
return rc;
}
@@ -361,6 +576,8 @@ static void idxd_user_drv_remove(struct idxd_dev *idxd_dev)
idxd_wq_del_cdev(wq);
drv_disable_wq(wq);
wq->type = IDXD_WQT_NONE;
destroy_workqueue(wq->wq);
wq->wq = NULL;
mutex_unlock(&wq->wq_lock);
}
@@ -407,3 +624,70 @@ void idxd_cdev_remove(void)
ida_destroy(&ictx[i].minor_ida);
}
}
/**
* idxd_copy_cr - copy completion record to user address space found by wq and
* PASID
* @wq: work queue
* @pasid: PASID
* @addr: user fault address to write
* @cr: completion record
* @len: number of bytes to copy
*
* This is called by a work that handles completion record fault.
*
* Return: number of bytes copied.
*/
int idxd_copy_cr(struct idxd_wq *wq, ioasid_t pasid, unsigned long addr,
void *cr, int len)
{
struct device *dev = &wq->idxd->pdev->dev;
int left = len, status_size = 1;
struct idxd_user_context *ctx;
struct mm_struct *mm;
mutex_lock(&wq->uc_lock);
ctx = xa_load(&wq->upasid_xa, pasid);
if (!ctx) {
dev_warn(dev, "No user context\n");
goto out;
}
mm = ctx->mm;
/*
* The completion record fault handling work is running in kernel
* thread context. It temporarily switches to the mm to copy cr
* to addr in the mm.
*/
kthread_use_mm(mm);
left = copy_to_user((void __user *)addr + status_size, cr + status_size,
len - status_size);
/*
* Copy status only after the rest of completion record is copied
* successfully so that the user gets the complete completion record
* when a non-zero status is polled.
*/
if (!left) {
u8 status;
/*
* Ensure that the completion record's status field is written
* after the rest of the completion record has been written.
* This ensures that the user receives the correct completion
* record information once polling for a non-zero status.
*/
wmb();
status = *(u8 *)cr;
if (put_user(status, (u8 __user *)addr))
left += status_size;
} else {
left += status_size;
}
kthread_unuse_mm(mm);
out:
mutex_unlock(&wq->uc_lock);
return len - left;
}

138
drivers/dma/idxd/debugfs.c Normal file
View File

@@ -0,0 +1,138 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2021 Intel Corporation. All rights rsvd. */
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/debugfs.h>
#include <linux/io-64-nonatomic-lo-hi.h>
#include <uapi/linux/idxd.h>
#include "idxd.h"
#include "registers.h"
static struct dentry *idxd_debugfs_dir;
static void dump_event_entry(struct idxd_device *idxd, struct seq_file *s,
u16 index, int *count, bool processed)
{
struct idxd_evl *evl = idxd->evl;
struct dsa_evl_entry *entry;
struct dsa_completion_record *cr;
u64 *raw;
int i;
int evl_strides = evl_ent_size(idxd) / sizeof(u64);
entry = (struct dsa_evl_entry *)evl->log + index;
if (!entry->e.desc_valid)
return;
seq_printf(s, "Event Log entry %d (real index %u) processed: %u\n",
*count, index, processed);
seq_printf(s, "desc valid %u wq idx valid %u\n"
"batch %u fault rw %u priv %u error 0x%x\n"
"wq idx %u op %#x pasid %u batch idx %u\n"
"fault addr %#llx\n",
entry->e.desc_valid, entry->e.wq_idx_valid,
entry->e.batch, entry->e.fault_rw, entry->e.priv,
entry->e.error, entry->e.wq_idx, entry->e.operation,
entry->e.pasid, entry->e.batch_idx, entry->e.fault_addr);
cr = &entry->cr;
seq_printf(s, "status %#x result %#x fault_info %#x bytes_completed %u\n"
"fault addr %#llx inv flags %#x\n\n",
cr->status, cr->result, cr->fault_info, cr->bytes_completed,
cr->fault_addr, cr->invalid_flags);
raw = (u64 *)entry;
for (i = 0; i < evl_strides; i++)
seq_printf(s, "entry[%d] = %#llx\n", i, raw[i]);
seq_puts(s, "\n");
*count += 1;
}
static int debugfs_evl_show(struct seq_file *s, void *d)
{
struct idxd_device *idxd = s->private;
struct idxd_evl *evl = idxd->evl;
union evl_status_reg evl_status;
u16 h, t, evl_size, i;
int count = 0;
bool processed = true;
if (!evl || !evl->log)
return 0;
spin_lock(&evl->lock);
h = evl->head;
evl_status.bits = ioread64(idxd->reg_base + IDXD_EVLSTATUS_OFFSET);
t = evl_status.tail;
evl_size = evl->size;
seq_printf(s, "Event Log head %u tail %u interrupt pending %u\n\n",
evl_status.head, evl_status.tail, evl_status.int_pending);
i = t;
while (1) {
i = (i + 1) % evl_size;
if (i == t)
break;
if (processed && i == h)
processed = false;
dump_event_entry(idxd, s, i, &count, processed);
}
spin_unlock(&evl->lock);
return 0;
}
DEFINE_SHOW_ATTRIBUTE(debugfs_evl);
int idxd_device_init_debugfs(struct idxd_device *idxd)
{
if (IS_ERR_OR_NULL(idxd_debugfs_dir))
return 0;
idxd->dbgfs_dir = debugfs_create_dir(dev_name(idxd_confdev(idxd)), idxd_debugfs_dir);
if (IS_ERR(idxd->dbgfs_dir))
return PTR_ERR(idxd->dbgfs_dir);
if (idxd->evl) {
idxd->dbgfs_evl_file = debugfs_create_file("event_log", 0400,
idxd->dbgfs_dir, idxd,
&debugfs_evl_fops);
if (IS_ERR(idxd->dbgfs_evl_file)) {
debugfs_remove_recursive(idxd->dbgfs_dir);
idxd->dbgfs_dir = NULL;
return PTR_ERR(idxd->dbgfs_evl_file);
}
}
return 0;
}
void idxd_device_remove_debugfs(struct idxd_device *idxd)
{
debugfs_remove_recursive(idxd->dbgfs_dir);
}
int idxd_init_debugfs(void)
{
if (!debugfs_initialized())
return 0;
idxd_debugfs_dir = debugfs_create_dir(KBUILD_MODNAME, NULL);
if (IS_ERR(idxd_debugfs_dir))
return PTR_ERR(idxd_debugfs_dir);
return 0;
}
void idxd_remove_debugfs(void)
{
debugfs_remove_recursive(idxd_debugfs_dir);
}

View File

@@ -752,6 +752,101 @@ void idxd_device_clear_state(struct idxd_device *idxd)
spin_unlock(&idxd->dev_lock);
}
static int idxd_device_evl_setup(struct idxd_device *idxd)
{
union gencfg_reg gencfg;
union evlcfg_reg evlcfg;
union genctrl_reg genctrl;
struct device *dev = &idxd->pdev->dev;
void *addr;
dma_addr_t dma_addr;
int size;
struct idxd_evl *evl = idxd->evl;
unsigned long *bmap;
int rc;
if (!evl)
return 0;
size = evl_size(idxd);
bmap = bitmap_zalloc(size, GFP_KERNEL);
if (!bmap) {
rc = -ENOMEM;
goto err_bmap;
}
/*
* Address needs to be page aligned. However, dma_alloc_coherent() provides
* at minimal page size aligned address. No manual alignment required.
*/
addr = dma_alloc_coherent(dev, size, &dma_addr, GFP_KERNEL);
if (!addr) {
rc = -ENOMEM;
goto err_alloc;
}
memset(addr, 0, size);
spin_lock(&evl->lock);
evl->log = addr;
evl->dma = dma_addr;
evl->log_size = size;
evl->bmap = bmap;
memset(&evlcfg, 0, sizeof(evlcfg));
evlcfg.bits[0] = dma_addr & GENMASK(63, 12);
evlcfg.size = evl->size;
iowrite64(evlcfg.bits[0], idxd->reg_base + IDXD_EVLCFG_OFFSET);
iowrite64(evlcfg.bits[1], idxd->reg_base + IDXD_EVLCFG_OFFSET + 8);
genctrl.bits = ioread32(idxd->reg_base + IDXD_GENCTRL_OFFSET);
genctrl.evl_int_en = 1;
iowrite32(genctrl.bits, idxd->reg_base + IDXD_GENCTRL_OFFSET);
gencfg.bits = ioread32(idxd->reg_base + IDXD_GENCFG_OFFSET);
gencfg.evl_en = 1;
iowrite32(gencfg.bits, idxd->reg_base + IDXD_GENCFG_OFFSET);
spin_unlock(&evl->lock);
return 0;
err_alloc:
bitmap_free(bmap);
err_bmap:
return rc;
}
static void idxd_device_evl_free(struct idxd_device *idxd)
{
union gencfg_reg gencfg;
union genctrl_reg genctrl;
struct device *dev = &idxd->pdev->dev;
struct idxd_evl *evl = idxd->evl;
gencfg.bits = ioread32(idxd->reg_base + IDXD_GENCFG_OFFSET);
if (!gencfg.evl_en)
return;
spin_lock(&evl->lock);
gencfg.evl_en = 0;
iowrite32(gencfg.bits, idxd->reg_base + IDXD_GENCFG_OFFSET);
genctrl.bits = ioread32(idxd->reg_base + IDXD_GENCTRL_OFFSET);
genctrl.evl_int_en = 0;
iowrite32(genctrl.bits, idxd->reg_base + IDXD_GENCTRL_OFFSET);
iowrite64(0, idxd->reg_base + IDXD_EVLCFG_OFFSET);
iowrite64(0, idxd->reg_base + IDXD_EVLCFG_OFFSET + 8);
dma_free_coherent(dev, evl->log_size, evl->log, evl->dma);
bitmap_free(evl->bmap);
evl->log = NULL;
evl->size = IDXD_EVL_SIZE_MIN;
spin_unlock(&evl->lock);
}
static void idxd_group_config_write(struct idxd_group *group)
{
struct idxd_device *idxd = group->idxd;
@@ -872,12 +967,16 @@ static int idxd_wq_config_write(struct idxd_wq *wq)
wq->wqcfg->priority = wq->priority;
if (idxd->hw.gen_cap.block_on_fault &&
test_bit(WQ_FLAG_BLOCK_ON_FAULT, &wq->flags))
test_bit(WQ_FLAG_BLOCK_ON_FAULT, &wq->flags) &&
!test_bit(WQ_FLAG_PRS_DISABLE, &wq->flags))
wq->wqcfg->bof = 1;
if (idxd->hw.wq_cap.wq_ats_support)
wq->wqcfg->wq_ats_disable = test_bit(WQ_FLAG_ATS_DISABLE, &wq->flags);
if (idxd->hw.wq_cap.wq_prs_support)
wq->wqcfg->wq_prs_disable = test_bit(WQ_FLAG_PRS_DISABLE, &wq->flags);
/* bytes 12-15 */
wq->wqcfg->max_xfer_shift = ilog2(wq->max_xfer_bytes);
idxd_wqcfg_set_max_batch_shift(idxd->data->type, wq->wqcfg, ilog2(wq->max_batch_size));
@@ -1451,15 +1550,24 @@ int idxd_device_drv_probe(struct idxd_dev *idxd_dev)
if (rc < 0)
return -ENXIO;
rc = idxd_device_evl_setup(idxd);
if (rc < 0) {
idxd->cmd_status = IDXD_SCMD_DEV_EVL_ERR;
return rc;
}
/* Start device */
rc = idxd_device_enable(idxd);
if (rc < 0)
if (rc < 0) {
idxd_device_evl_free(idxd);
return rc;
}
/* Setup DMA device without channels */
rc = idxd_register_dma_device(idxd);
if (rc < 0) {
idxd_device_disable(idxd);
idxd_device_evl_free(idxd);
idxd->cmd_status = IDXD_SCMD_DEV_DMA_ERR;
return rc;
}
@@ -1488,6 +1596,7 @@ void idxd_device_drv_remove(struct idxd_dev *idxd_dev)
idxd_device_disable(idxd);
if (test_bit(IDXD_FLAG_CONFIGURABLE, &idxd->flags))
idxd_device_reset(idxd);
idxd_device_evl_free(idxd);
}
static enum idxd_dev_type dev_types[] = {

View File

@@ -32,6 +32,7 @@ enum idxd_dev_type {
IDXD_DEV_GROUP,
IDXD_DEV_ENGINE,
IDXD_DEV_CDEV,
IDXD_DEV_CDEV_FILE,
IDXD_DEV_MAX_TYPE,
};
@@ -127,6 +128,12 @@ struct idxd_pmu {
#define IDXD_MAX_PRIORITY 0xf
enum {
COUNTER_FAULTS = 0,
COUNTER_FAULT_FAILS,
COUNTER_MAX
};
enum idxd_wq_state {
IDXD_WQ_DISABLED = 0,
IDXD_WQ_ENABLED,
@@ -136,6 +143,7 @@ enum idxd_wq_flag {
WQ_FLAG_DEDICATED = 0,
WQ_FLAG_BLOCK_ON_FAULT,
WQ_FLAG_ATS_DISABLE,
WQ_FLAG_PRS_DISABLE,
};
enum idxd_wq_type {
@@ -185,6 +193,7 @@ struct idxd_wq {
struct idxd_dev idxd_dev;
struct idxd_cdev *idxd_cdev;
struct wait_queue_head err_queue;
struct workqueue_struct *wq;
struct idxd_device *idxd;
int id;
struct idxd_irq_entry ie;
@@ -214,6 +223,10 @@ struct idxd_wq {
char name[WQ_NAME_SIZE + 1];
u64 max_xfer_bytes;
u32 max_batch_size;
/* Lock to protect upasid_xa access. */
struct mutex uc_lock;
struct xarray upasid_xa;
};
struct idxd_engine {
@@ -232,6 +245,7 @@ struct idxd_hw {
union engine_cap_reg engine_cap;
struct opcap opcap;
u32 cmd_cap;
union iaa_cap_reg iaa_cap;
};
enum idxd_device_state {
@@ -258,6 +272,32 @@ struct idxd_driver_data {
struct device_type *dev_type;
int compl_size;
int align;
int evl_cr_off;
int cr_status_off;
int cr_result_off;
};
struct idxd_evl {
/* Lock to protect event log access. */
spinlock_t lock;
void *log;
dma_addr_t dma;
/* Total size of event log = number of entries * entry size. */
unsigned int log_size;
/* The number of entries in the event log. */
u16 size;
u16 head;
unsigned long *bmap;
bool batch_fail[IDXD_MAX_BATCH_IDENT];
};
struct idxd_evl_fault {
struct work_struct work;
struct idxd_wq *wq;
u8 status;
/* make this last member always */
struct __evl_entry entry[];
};
struct idxd_device {
@@ -316,8 +356,24 @@ struct idxd_device {
struct idxd_pmu *idxd_pmu;
unsigned long *opcap_bmap;
struct idxd_evl *evl;
struct kmem_cache *evl_cache;
struct dentry *dbgfs_dir;
struct dentry *dbgfs_evl_file;
};
static inline unsigned int evl_ent_size(struct idxd_device *idxd)
{
return idxd->hw.gen_cap.evl_support ?
(32 * (1 << idxd->hw.gen_cap.evl_support)) : 0;
}
static inline unsigned int evl_size(struct idxd_device *idxd)
{
return idxd->evl->size * evl_ent_size(idxd);
}
/* IDXD software descriptor */
struct idxd_desc {
union {
@@ -351,6 +407,7 @@ enum idxd_completion_status {
#define engine_confdev(engine) &engine->idxd_dev.conf_dev
#define group_confdev(group) &group->idxd_dev.conf_dev
#define cdev_dev(cdev) &cdev->idxd_dev.conf_dev
#define user_ctx_dev(ctx) (&(ctx)->idxd_dev.conf_dev)
#define confdev_to_idxd_dev(dev) container_of(dev, struct idxd_dev, conf_dev)
#define idxd_dev_to_idxd(idxd_dev) container_of(idxd_dev, struct idxd_device, idxd_dev)
@@ -598,6 +655,7 @@ int idxd_register_driver(void);
void idxd_unregister_driver(void);
void idxd_wqs_quiesce(struct idxd_device *idxd);
bool idxd_queue_int_handle_resubmit(struct idxd_desc *desc);
void multi_u64_to_bmap(unsigned long *bmap, u64 *val, int count);
/* device interrupt control */
irqreturn_t idxd_misc_thread(int vec, void *data);
@@ -662,6 +720,9 @@ void idxd_cdev_remove(void);
int idxd_cdev_get_major(struct idxd_device *idxd);
int idxd_wq_add_cdev(struct idxd_wq *wq);
void idxd_wq_del_cdev(struct idxd_wq *wq);
int idxd_copy_cr(struct idxd_wq *wq, ioasid_t pasid, unsigned long addr,
void *buf, int len);
void idxd_user_counter_increment(struct idxd_wq *wq, u32 pasid, int index);
/* perfmon */
#if IS_ENABLED(CONFIG_INTEL_IDXD_PERFMON)
@@ -678,4 +739,10 @@ static inline void perfmon_init(void) {}
static inline void perfmon_exit(void) {}
#endif
/* debugfs */
int idxd_device_init_debugfs(struct idxd_device *idxd);
void idxd_device_remove_debugfs(struct idxd_device *idxd);
int idxd_init_debugfs(void);
void idxd_remove_debugfs(void);
#endif

Some files were not shown because too many files have changed in this diff Show More