Skip to content

Commit b7d68d5

Browse files
committed
dmaengine: sdxi: dma: Set up and use interrupt for completion
Booting with: sdxi.enabled=1 sdxi.dma_engine=1 dmatest.polled=0 dmatest.iterations=5 dmatest.run=1 dmatest.threads_per_chan=1 [ 3.389650][ T1] dmatest: Started 1 threads using dma0chan0 [ 3.390157][ T66] sdxi 0000:00:03.0: vchan ffff888102b72628: txd ffff888109b147c0[2]: submitted [ 3.392224][ T66] sdxi 0000:00:03.0: Ringing context 1 (state = running) doorbell: 2 [ 3.392280][ C3] sdxi 0000:00:03.0: hello from sdxi_dma_cxt_irq [ 3.394409][ C3] sdxi 0000:00:03.0: txd ffff888109b147c0[2]: marked complete [ 3.399734][ T66] dmatest: dma0chan0-copy0: verifying source buffer... [ 3.401691][ T66] dmatest: dma0chan0-copy0: verifying dest buffer... [ 3.402830][ T66] dmatest: dma0chan0-copy0: result #1: 'test passed' with src_off=0x2e4c dst_off=0x272 len=0x87a (0) [ 3.404854][ T66] sdxi 0000:00:03.0: vchan ffff888102b72628: txd ffff888109b14c40[3]: submitted [ 3.406048][ T66] sdxi 0000:00:03.0: Ringing context 1 (state = running) doorbell: 4 [ 3.406102][ C3] sdxi 0000:00:03.0: hello from sdxi_dma_cxt_irq [ 3.408945][ C3] sdxi 0000:00:03.0: txd ffff888109b14c40[3]: marked complete [ 3.411967][ T66] dmatest: dma0chan0-copy0: verifying source buffer... [ 3.412921][ T66] dmatest: dma0chan0-copy0: verifying dest buffer... [ 3.415894][ T66] dmatest: dma0chan0-copy0: result #2: 'test passed' with src_off=0x8b dst_off=0xaa len=0x3db2 (0) [ 3.417497][ T66] sdxi 0000:00:03.0: vchan ffff888102b72628: txd ffff888109b14dc0[4]: submitted [ 3.418618][ T66] sdxi 0000:00:03.0: Ringing context 1 (state = running) doorbell: 6 [ 3.420148][ C3] sdxi 0000:00:03.0: hello from sdxi_dma_cxt_irq [ 3.420966][ C3] sdxi 0000:00:03.0: txd ffff888109b14dc0[4]: marked complete [ 3.423185][ T66] dmatest: dma0chan0-copy0: verifying source buffer... [ 3.424108][ T66] dmatest: dma0chan0-copy0: verifying dest buffer... [ 3.424958][ T66] dmatest: dma0chan0-copy0: result #3: 'test passed' with src_off=0x64 dst_off=0x45 len=0x3f6e (0) [ 3.426565][ T66] sdxi 0000:00:03.0: vchan ffff888102b72628: txd ffff888109b14f40[5]: submitted [ 3.427691][ T66] sdxi 0000:00:03.0: Ringing context 1 (state = running) doorbell: 8 [ 3.428769][ C3] sdxi 0000:00:03.0: hello from sdxi_dma_cxt_irq [ 3.429615][ C3] sdxi 0000:00:03.0: txd ffff888109b14f40[5]: marked complete [ 3.431692][ T66] dmatest: dma0chan0-copy0: verifying source buffer... [ 3.432662][ T66] dmatest: dma0chan0-copy0: verifying dest buffer... [ 3.433557][ T66] dmatest: dma0chan0-copy0: result #4: 'test passed' with src_off=0x1b88 dst_off=0x1845 len=0xa89 (0) [ 3.435143][ T66] sdxi 0000:00:03.0: vchan ffff888102b72628: txd ffff888109b150c0[6]: submitted [ 3.436340][ T66] sdxi 0000:00:03.0: Ringing context 1 (state = running) doorbell: 10 [ 3.437374][ C3] sdxi 0000:00:03.0: hello from sdxi_dma_cxt_irq [ 3.438198][ C3] sdxi 0000:00:03.0: txd ffff888109b150c0[6]: marked complete [ 3.440330][ T66] dmatest: dma0chan0-copy0: verifying source buffer... [ 3.441247][ T66] dmatest: dma0chan0-copy0: verifying dest buffer... [ 3.442169][ T66] dmatest: dma0chan0-copy0: result #5: 'test passed' with src_off=0xc80 dst_off=0x82e len=0x327c (0) [ 3.443599][ T66] dmatest: dma0chan0-copy0: summary 5 tests, 0 failures 122.00 iops 1171 KB/s (0) [ 3.445010][ T66] dma0chan0-copy0 (66) used greatest stack depth: 29224 bytes left Signed-off-by: Nathan Lynch <nathan.lynch@amd.com>
1 parent c8f881b commit b7d68d5

1 file changed

Lines changed: 118 additions & 76 deletions

File tree

drivers/dma/sdxi/dma.c

Lines changed: 118 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <linux/dma-mapping.h>
1212
#include <linux/dmaengine.h>
1313
#include <linux/list.h>
14+
#include <linux/pci.h>
1415
#include <linux/spinlock.h>
1516

1617
#include "../dmaengine.h"
@@ -54,6 +55,7 @@
5455
struct sdxi_dma_chan {
5556
struct virt_dma_chan vchan;
5657
struct sdxi_cxt *cxt;
58+
u16 intr_akey;
5759
};
5860

5961
/*
@@ -62,8 +64,6 @@ struct sdxi_dma_chan {
6264
struct sdxi_dma_desc {
6365
struct virt_dma_desc vdesc;
6466
struct sdxi_ring_resv resv;
65-
struct sdxi_cst_blk *cst_blk;
66-
dma_addr_t cst_blk_dma;
6767
struct sdxi_completion *completion; // Should this be optional? Maybe there should always be one completion per txd.
6868
};
6969

@@ -85,22 +85,75 @@ static void sdxi_tx_desc_free(struct virt_dma_desc *vdesc)
8585
{
8686
struct sdxi_dma_desc *sddesc = to_sdxi_dma_desc(vdesc);
8787

88-
if (sddesc->cst_blk)
89-
pr_err_ratelimited("leaking cst_blk for %d\n", vdesc->tx.cookie);
90-
if (sddesc->completion)
91-
sdxi_completion_free(sddesc->completion);
88+
sdxi_completion_free(sddesc->completion);
9289
kfree(to_sdxi_dma_desc(vdesc));
9390
}
9491

92+
static struct sdxi_dma_desc *
93+
prep_memcpy_intr(struct dma_chan *dma_chan, const struct sdxi_copy *params)
94+
{
95+
struct sdxi_cxt *cxt = to_sdxi_dma_chan(dma_chan)->cxt;
96+
struct sdxi_completion *completion __free(sdxi_completion) = NULL;
97+
struct sdxi_dma_desc *sddesc __free(kfree) = NULL;
98+
struct sdxi_desc *copy, *intr;
99+
100+
completion = sdxi_completion_alloc(cxt->sdxi);
101+
if (!completion)
102+
return NULL;
103+
104+
sddesc = kzalloc(sizeof(*sddesc), GFP_NOWAIT);
105+
if (!sddesc)
106+
return NULL;
107+
108+
if (sdxi_ring_reserve(cxt->ring_state, 2, &sddesc->resv))
109+
return NULL;
110+
111+
copy = sdxi_ring_resv_next(&sddesc->resv);
112+
(void)sdxi_encode_copy(copy, params); /* Caller checked validity. */
113+
sdxi_completion_attach(copy, completion);
114+
115+
sddesc->completion = no_free_ptr(completion);
116+
117+
intr = sdxi_ring_resv_next(&sddesc->resv);
118+
sdxi_encode_intr(intr, &(const struct sdxi_intr) {
119+
.akey = to_sdxi_dma_chan(dma_chan)->intr_akey,
120+
});
121+
sdxi_desc_set_fence(intr);
122+
return_ptr(sddesc);
123+
}
124+
125+
static struct sdxi_dma_desc *
126+
prep_memcpy_polled(struct dma_chan *dma_chan, const struct sdxi_copy *params)
127+
{
128+
struct sdxi_cxt *cxt = to_sdxi_dma_chan(dma_chan)->cxt;
129+
struct sdxi_completion *completion __free(sdxi_completion) = NULL;
130+
struct sdxi_dma_desc *sddesc __free(kfree) = NULL;
131+
struct sdxi_desc *copy;
132+
133+
completion = sdxi_completion_alloc(cxt->sdxi);
134+
if (!completion)
135+
return NULL;
136+
137+
sddesc = kzalloc(sizeof(*sddesc), GFP_NOWAIT);
138+
if (!sddesc)
139+
return NULL;
140+
141+
if (sdxi_ring_reserve(cxt->ring_state, 1, &sddesc->resv))
142+
return NULL;
143+
144+
copy = sdxi_ring_resv_next(&sddesc->resv);
145+
(void)sdxi_encode_copy(copy, params); /* Caller checked validity. */
146+
sdxi_completion_attach(copy, completion);
147+
148+
sddesc->completion = no_free_ptr(completion);
149+
return_ptr(sddesc);
150+
}
151+
95152
static struct dma_async_tx_descriptor *
96153
sdxi_dma_prep_memcpy(struct dma_chan *dma_chan, dma_addr_t dst,
97154
dma_addr_t src, size_t len, unsigned long flags)
98155
{
99-
struct dma_async_tx_descriptor *txd;
100-
struct sdxi_dma_desc *sddesc __free(kfree) = NULL;
101-
struct sdxi_cst_blk *cst_blk;
102-
dma_addr_t cst_blk_dma;
103-
struct sdxi_cxt *cxt = to_sdxi_dma_chan(dma_chan)->cxt;
156+
struct sdxi_dma_desc *sddesc;
104157
struct sdxi_copy copy = {
105158
.src = src,
106159
.dst = dst,
@@ -109,25 +162,6 @@ sdxi_dma_prep_memcpy(struct dma_chan *dma_chan, dma_addr_t dst,
109162
.len = len,
110163
};
111164

112-
/* FIXME: honor DMA_PREP_FENCE by appending nop with fe=1. */
113-
114-
/*
115-
* Notes:
116-
*
117-
* Always reserve 2, write a nop to the second one. Issue
118-
* pending can overwrite the final vdesc's nop with an
119-
* interrupt command. This way we avoid interrupting on every
120-
* vdesc.
121-
*
122-
* Avoid setting fe bit until final interrupt desc to allow
123-
* concurrency in descriptor processing by the engine.
124-
*/
125-
126-
/*
127-
* Sorry, no interrupt-signaled completion yet.
128-
*/
129-
if (WARN_ON_ONCE(flags & DMA_PREP_INTERRUPT))
130-
return NULL;
131165
/*
132166
* temp hack: perform a trial encode to a dummy descriptor on
133167
* the stack so we can reject bad inputs without touching the
@@ -136,46 +170,11 @@ sdxi_dma_prep_memcpy(struct dma_chan *dma_chan, dma_addr_t dst,
136170
if (sdxi_encode_copy(&(struct sdxi_desc){}, &copy))
137171
return NULL;
138172

139-
/* FIXME: use a pool? this is wasteful. */
140-
/* FIXME also: this leaks when the reservation fails.*/
141-
cst_blk = dma_alloc_coherent(sdxi_to_dev(cxt->sdxi), sizeof(*cst_blk),
142-
&cst_blk_dma, GFP_NOWAIT);
143-
if (!cst_blk)
144-
return NULL;
145-
146-
cst_blk->signal = cpu_to_le64(1);
147-
148-
sddesc = kmalloc(sizeof(*sddesc), GFP_NOWAIT);
149-
if (!sddesc)
150-
return NULL;
151-
152-
*sddesc = (typeof(*sddesc)) {
153-
.cst_blk = cst_blk,
154-
.cst_blk_dma = cst_blk_dma,
155-
};
156-
157-
if (sdxi_ring_reserve(cxt->ring_state, 1, &sddesc->resv))
158-
return NULL;
159-
160-
struct sdxi_desc *hwdesc = sdxi_ring_resv_next(&sddesc->resv);
173+
sddesc = (flags & DMA_PREP_INTERRUPT) ?
174+
prep_memcpy_intr(dma_chan, &copy) :
175+
prep_memcpy_polled(dma_chan, &copy);
161176

162-
(void)sdxi_encode_copy(hwdesc, &copy);
163-
sdxi_desc_set_csb(hwdesc, cst_blk_dma);
164-
165-
txd = vchan_tx_prep(to_virt_chan(dma_chan), &sddesc->vdesc, flags);
166-
retain_and_null_ptr(sddesc);
167-
return txd;
168-
}
169-
170-
#if 0
171-
static bool sdxi_cst_blk_erred(const struct sdxi_cst_blk *cst)
172-
{
173-
return FIELD_GET(SDXI_CST_BLK_ER_BIT, le32_to_cpu(cst->flags));
174-
}
175-
176-
static bool sdxi_cst_blk_complete(const struct sdxi_cst_blk *cst)
177-
{
178-
return cst->signal == 0;
177+
return vchan_tx_prep(to_virt_chan(dma_chan), &sddesc->vdesc, flags);
179178
}
180179

181180
static enum dma_status sdxi_tx_status(struct dma_chan *chan,
@@ -199,16 +198,20 @@ static enum dma_status sdxi_tx_status(struct dma_chan *chan,
199198

200199
sddesc = to_sdxi_dma_desc(vdesc);
201200

202-
if (sdxi_cst_blk_erred(sddesc->cst_blk))
201+
/* Maybe we should always set up a completion even with DMA_PREP_INTERRUPT? */
202+
if (WARN_ON_ONCE(!sddesc->completion))
203+
return DMA_ERROR;
204+
205+
if (!sdxi_completion_signaled(sddesc->completion))
206+
return DMA_IN_PROGRESS;
207+
208+
if (sdxi_completion_errored(sddesc->completion))
203209
return DMA_ERROR;
204210

205-
/* fixme? should this happen? */
206-
if (sdxi_cst_blk_complete(sddesc->cst_blk))
207-
return DMA_COMPLETE;
211+
vchan_cookie_complete(vdesc);
208212

209-
return DMA_IN_PROGRESS;
213+
return dma_cookie_status(chan, cookie, state);
210214
}
211-
#endif
212215

213216
static void sdxi_dma_issue_pending(struct dma_chan *dma_chan)
214217
{
@@ -308,6 +311,28 @@ static void sdxi_dma_synchronize(struct dma_chan *dma_chan)
308311
vchan_synchronize(to_virt_chan(dma_chan));
309312
}
310313

314+
static irqreturn_t sdxi_dma_cxt_irq(int irq, void *data)
315+
{
316+
struct sdxi_dma_chan *sdchan = data;
317+
struct virt_dma_chan *vchan = &sdchan->vchan;
318+
319+
sdxi_info(sdchan->cxt->sdxi, "hello from %s\n", __func__);
320+
321+
guard(spinlock_irqsave)(&vchan->lock);
322+
323+
for (struct virt_dma_desc *vdesc = vchan_next_desc(vchan);
324+
vdesc; vdesc = vchan_next_desc(vchan)) {
325+
struct sdxi_dma_desc *sddesc = to_sdxi_dma_desc(vdesc);
326+
327+
if (!sdxi_completion_signaled(sddesc->completion))
328+
continue;
329+
list_del(&vdesc->node);
330+
vchan_cookie_complete(&sddesc->vdesc);
331+
}
332+
333+
return IRQ_HANDLED;
334+
}
335+
311336
static int sdxi_dma_alloc_chan_resources(struct dma_chan *dma_chan)
312337
{
313338
return sdxi_adm_start_cxt(to_sdxi_dma_chan(dma_chan)->cxt);
@@ -319,10 +344,15 @@ static void sdxi_dma_free_chan_resources(struct dma_chan *dma_chan)
319344
vchan_free_chan_resources(to_virt_chan(dma_chan));
320345
}
321346

347+
#define BAD_HARDCODED_MSG 1
348+
#define BAD_HARDCODED_AKEY_IDX 1
349+
322350
static void add_channel(struct dma_device *dma_dev)
323351
{
324352
struct sdxi_dma_chan *sdchan;
325353
struct sdxi_dev *sdxi = dev_get_drvdata(dma_dev->dev);
354+
unsigned int irq = pci_irq_vector(to_pci_dev(sdxi_to_dev(sdxi)),
355+
BAD_HARDCODED_MSG);
326356

327357
sdchan = devm_kzalloc(dma_dev->dev, sizeof(*sdchan), GFP_KERNEL);
328358
if (!sdchan)
@@ -334,6 +364,18 @@ static void add_channel(struct dma_device *dma_dev)
334364
return;
335365
}
336366

367+
/* FIXME: remove PCI dependency and hardcoded irq */
368+
(void)request_irq(irq, sdxi_dma_cxt_irq,
369+
IRQF_TRIGGER_NONE, "SDXI DMAengine", sdchan);
370+
371+
/* FIXME: Add an akey allocation API, don't hardcode the index. */
372+
sdchan->cxt->akey_table->entry[BAD_HARDCODED_AKEY_IDX] = (struct sdxi_akey_ent) {
373+
.intr_num = cpu_to_le16(FIELD_PREP(SDXI_AKEY_ENT_VL, 1) |
374+
FIELD_PREP(SDXI_AKEY_ENT_IV, 1) |
375+
FIELD_PREP(SDXI_AKEY_ENT_INTR_NUM,
376+
BAD_HARDCODED_MSG)),
377+
};
378+
sdchan->intr_akey = BAD_HARDCODED_AKEY_IDX;
337379
sdchan->vchan.desc_free = sdxi_tx_desc_free;
338380
vchan_init(&sdchan->vchan, dma_dev);
339381
}
@@ -370,7 +412,7 @@ int sdxi_dma_register(struct sdxi_dev *sdxi)
370412
.device_resume = NULL, /* fixme */
371413
.device_terminate_all = sdxi_dma_terminate_all,
372414
.device_synchronize = sdxi_dma_synchronize,
373-
.device_tx_status = dma_cookie_status,
415+
.device_tx_status = sdxi_tx_status,
374416
.device_issue_pending = sdxi_dma_issue_pending,
375417
.device_release = NULL, /* fixme */
376418
};

0 commit comments

Comments
 (0)