diff options
author | Dan Williams <dan.j.williams@intel.com> | 2007-01-02 13:10:43 -0500 |
---|---|---|
committer | Dan Williams <dan.j.williams@intel.com> | 2007-07-13 11:06:11 -0400 |
commit | 7405f74badf46b5d023c5d2b670b4471525f6c91 (patch) | |
tree | 20dd20571637dba1c2b04c7b13ac208c33b5706b /drivers/dma | |
parent | 428ed6024fa74a271142f3257966e9b5e1cb37a1 (diff) |
dmaengine: refactor dmaengine around dma_async_tx_descriptor
The current dmaengine interface defines mutliple routines per operation,
i.e. dma_async_memcpy_buf_to_buf, dma_async_memcpy_buf_to_page etc. Adding
more operation types (xor, crc, etc) to this model would result in an
unmanageable number of method permutations.
Are we really going to add a set of hooks for each DMA engine
whizbang feature?
- Jeff Garzik
The descriptor creation process is refactored using the new common
dma_async_tx_descriptor structure. Instead of per driver
do_<operation>_<dest>_to_<src> methods, drivers integrate
dma_async_tx_descriptor into their private software descriptor and then
define a 'prep' routine per operation. The prep routine allocates a
descriptor and ensures that the tx_set_src, tx_set_dest, tx_submit routines
are valid. Descriptor creation and submission becomes:
struct dma_device *dev;
struct dma_chan *chan;
struct dma_async_tx_descriptor *tx;
tx = dev->device_prep_dma_<operation>(chan, len, int_flag)
tx->tx_set_src(dma_addr_t, tx, index /* for multi-source ops */)
tx->tx_set_dest(dma_addr_t, tx, index)
tx->tx_submit(tx)
In addition to the refactoring, dma_async_tx_descriptor also lays the
groundwork for definining cross-channel-operation dependencies, and a
callback facility for asynchronous notification of operation completion.
Changelog:
* drop dma mapping methods, suggested by Chris Leech
* fix ioat_dma_dependency_added, also caught by Andrew Morton
* fix dma_sync_wait, change from Andrew Morton
* uninline large functions, change from Andrew Morton
* add tx->callback = NULL to dmaengine calls to interoperate with async_tx
calls
* hookup ioat_tx_submit
* convert channel capabilities to a 'cpumask_t like' bitmap
* removed DMA_TX_ARRAY_INIT, no longer needed
* checkpatch.pl fixes
* make set_src, set_dest, and tx_submit descriptor specific methods
* fixup git-ioat merge
* move group_list and phys to dma_async_tx_descriptor
Cc: Jeff Garzik <jeff@garzik.org>
Cc: Chris Leech <christopher.leech@intel.com>
Signed-off-by: Shannon Nelson <shannon.nelson@intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Acked-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/dma')
-rw-r--r-- | drivers/dma/dmaengine.c | 182 | ||||
-rw-r--r-- | drivers/dma/ioatdma.c | 295 | ||||
-rw-r--r-- | drivers/dma/ioatdma.h | 13 |
3 files changed, 325 insertions, 165 deletions
diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index 828310d8be80..404cc7b6e705 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c | |||
@@ -59,6 +59,7 @@ | |||
59 | 59 | ||
60 | #include <linux/init.h> | 60 | #include <linux/init.h> |
61 | #include <linux/module.h> | 61 | #include <linux/module.h> |
62 | #include <linux/mm.h> | ||
62 | #include <linux/device.h> | 63 | #include <linux/device.h> |
63 | #include <linux/dmaengine.h> | 64 | #include <linux/dmaengine.h> |
64 | #include <linux/hardirq.h> | 65 | #include <linux/hardirq.h> |
@@ -66,6 +67,7 @@ | |||
66 | #include <linux/percpu.h> | 67 | #include <linux/percpu.h> |
67 | #include <linux/rcupdate.h> | 68 | #include <linux/rcupdate.h> |
68 | #include <linux/mutex.h> | 69 | #include <linux/mutex.h> |
70 | #include <linux/jiffies.h> | ||
69 | 71 | ||
70 | static DEFINE_MUTEX(dma_list_mutex); | 72 | static DEFINE_MUTEX(dma_list_mutex); |
71 | static LIST_HEAD(dma_device_list); | 73 | static LIST_HEAD(dma_device_list); |
@@ -165,6 +167,24 @@ static struct dma_chan *dma_client_chan_alloc(struct dma_client *client) | |||
165 | return NULL; | 167 | return NULL; |
166 | } | 168 | } |
167 | 169 | ||
170 | enum dma_status dma_sync_wait(struct dma_chan *chan, dma_cookie_t cookie) | ||
171 | { | ||
172 | enum dma_status status; | ||
173 | unsigned long dma_sync_wait_timeout = jiffies + msecs_to_jiffies(5000); | ||
174 | |||
175 | dma_async_issue_pending(chan); | ||
176 | do { | ||
177 | status = dma_async_is_tx_complete(chan, cookie, NULL, NULL); | ||
178 | if (time_after_eq(jiffies, dma_sync_wait_timeout)) { | ||
179 | printk(KERN_ERR "dma_sync_wait_timeout!\n"); | ||
180 | return DMA_ERROR; | ||
181 | } | ||
182 | } while (status == DMA_IN_PROGRESS); | ||
183 | |||
184 | return status; | ||
185 | } | ||
186 | EXPORT_SYMBOL(dma_sync_wait); | ||
187 | |||
168 | /** | 188 | /** |
169 | * dma_chan_cleanup - release a DMA channel's resources | 189 | * dma_chan_cleanup - release a DMA channel's resources |
170 | * @kref: kernel reference structure that contains the DMA channel device | 190 | * @kref: kernel reference structure that contains the DMA channel device |
@@ -322,6 +342,25 @@ int dma_async_device_register(struct dma_device *device) | |||
322 | if (!device) | 342 | if (!device) |
323 | return -ENODEV; | 343 | return -ENODEV; |
324 | 344 | ||
345 | /* validate device routines */ | ||
346 | BUG_ON(dma_has_cap(DMA_MEMCPY, device->cap_mask) && | ||
347 | !device->device_prep_dma_memcpy); | ||
348 | BUG_ON(dma_has_cap(DMA_XOR, device->cap_mask) && | ||
349 | !device->device_prep_dma_xor); | ||
350 | BUG_ON(dma_has_cap(DMA_ZERO_SUM, device->cap_mask) && | ||
351 | !device->device_prep_dma_zero_sum); | ||
352 | BUG_ON(dma_has_cap(DMA_MEMSET, device->cap_mask) && | ||
353 | !device->device_prep_dma_memset); | ||
354 | BUG_ON(dma_has_cap(DMA_ZERO_SUM, device->cap_mask) && | ||
355 | !device->device_prep_dma_interrupt); | ||
356 | |||
357 | BUG_ON(!device->device_alloc_chan_resources); | ||
358 | BUG_ON(!device->device_free_chan_resources); | ||
359 | BUG_ON(!device->device_dependency_added); | ||
360 | BUG_ON(!device->device_is_tx_complete); | ||
361 | BUG_ON(!device->device_issue_pending); | ||
362 | BUG_ON(!device->dev); | ||
363 | |||
325 | init_completion(&device->done); | 364 | init_completion(&device->done); |
326 | kref_init(&device->refcount); | 365 | kref_init(&device->refcount); |
327 | device->dev_id = id++; | 366 | device->dev_id = id++; |
@@ -415,6 +454,149 @@ void dma_async_device_unregister(struct dma_device *device) | |||
415 | } | 454 | } |
416 | EXPORT_SYMBOL(dma_async_device_unregister); | 455 | EXPORT_SYMBOL(dma_async_device_unregister); |
417 | 456 | ||
457 | /** | ||
458 | * dma_async_memcpy_buf_to_buf - offloaded copy between virtual addresses | ||
459 | * @chan: DMA channel to offload copy to | ||
460 | * @dest: destination address (virtual) | ||
461 | * @src: source address (virtual) | ||
462 | * @len: length | ||
463 | * | ||
464 | * Both @dest and @src must be mappable to a bus address according to the | ||
465 | * DMA mapping API rules for streaming mappings. | ||
466 | * Both @dest and @src must stay memory resident (kernel memory or locked | ||
467 | * user space pages). | ||
468 | */ | ||
469 | dma_cookie_t | ||
470 | dma_async_memcpy_buf_to_buf(struct dma_chan *chan, void *dest, | ||
471 | void *src, size_t len) | ||
472 | { | ||
473 | struct dma_device *dev = chan->device; | ||
474 | struct dma_async_tx_descriptor *tx; | ||
475 | dma_addr_t addr; | ||
476 | dma_cookie_t cookie; | ||
477 | int cpu; | ||
478 | |||
479 | tx = dev->device_prep_dma_memcpy(chan, len, 0); | ||
480 | if (!tx) | ||
481 | return -ENOMEM; | ||
482 | |||
483 | tx->ack = 1; | ||
484 | tx->callback = NULL; | ||
485 | addr = dma_map_single(dev->dev, src, len, DMA_TO_DEVICE); | ||
486 | tx->tx_set_src(addr, tx, 0); | ||
487 | addr = dma_map_single(dev->dev, dest, len, DMA_FROM_DEVICE); | ||
488 | tx->tx_set_dest(addr, tx, 0); | ||
489 | cookie = tx->tx_submit(tx); | ||
490 | |||
491 | cpu = get_cpu(); | ||
492 | per_cpu_ptr(chan->local, cpu)->bytes_transferred += len; | ||
493 | per_cpu_ptr(chan->local, cpu)->memcpy_count++; | ||
494 | put_cpu(); | ||
495 | |||
496 | return cookie; | ||
497 | } | ||
498 | EXPORT_SYMBOL(dma_async_memcpy_buf_to_buf); | ||
499 | |||
500 | /** | ||
501 | * dma_async_memcpy_buf_to_pg - offloaded copy from address to page | ||
502 | * @chan: DMA channel to offload copy to | ||
503 | * @page: destination page | ||
504 | * @offset: offset in page to copy to | ||
505 | * @kdata: source address (virtual) | ||
506 | * @len: length | ||
507 | * | ||
508 | * Both @page/@offset and @kdata must be mappable to a bus address according | ||
509 | * to the DMA mapping API rules for streaming mappings. | ||
510 | * Both @page/@offset and @kdata must stay memory resident (kernel memory or | ||
511 | * locked user space pages) | ||
512 | */ | ||
513 | dma_cookie_t | ||
514 | dma_async_memcpy_buf_to_pg(struct dma_chan *chan, struct page *page, | ||
515 | unsigned int offset, void *kdata, size_t len) | ||
516 | { | ||
517 | struct dma_device *dev = chan->device; | ||
518 | struct dma_async_tx_descriptor *tx; | ||
519 | dma_addr_t addr; | ||
520 | dma_cookie_t cookie; | ||
521 | int cpu; | ||
522 | |||
523 | tx = dev->device_prep_dma_memcpy(chan, len, 0); | ||
524 | if (!tx) | ||
525 | return -ENOMEM; | ||
526 | |||
527 | tx->ack = 1; | ||
528 | tx->callback = NULL; | ||
529 | addr = dma_map_single(dev->dev, kdata, len, DMA_TO_DEVICE); | ||
530 | tx->tx_set_src(addr, tx, 0); | ||
531 | addr = dma_map_page(dev->dev, page, offset, len, DMA_FROM_DEVICE); | ||
532 | tx->tx_set_dest(addr, tx, 0); | ||
533 | cookie = tx->tx_submit(tx); | ||
534 | |||
535 | cpu = get_cpu(); | ||
536 | per_cpu_ptr(chan->local, cpu)->bytes_transferred += len; | ||
537 | per_cpu_ptr(chan->local, cpu)->memcpy_count++; | ||
538 | put_cpu(); | ||
539 | |||
540 | return cookie; | ||
541 | } | ||
542 | EXPORT_SYMBOL(dma_async_memcpy_buf_to_pg); | ||
543 | |||
544 | /** | ||
545 | * dma_async_memcpy_pg_to_pg - offloaded copy from page to page | ||
546 | * @chan: DMA channel to offload copy to | ||
547 | * @dest_pg: destination page | ||
548 | * @dest_off: offset in page to copy to | ||
549 | * @src_pg: source page | ||
550 | * @src_off: offset in page to copy from | ||
551 | * @len: length | ||
552 | * | ||
553 | * Both @dest_page/@dest_off and @src_page/@src_off must be mappable to a bus | ||
554 | * address according to the DMA mapping API rules for streaming mappings. | ||
555 | * Both @dest_page/@dest_off and @src_page/@src_off must stay memory resident | ||
556 | * (kernel memory or locked user space pages). | ||
557 | */ | ||
558 | dma_cookie_t | ||
559 | dma_async_memcpy_pg_to_pg(struct dma_chan *chan, struct page *dest_pg, | ||
560 | unsigned int dest_off, struct page *src_pg, unsigned int src_off, | ||
561 | size_t len) | ||
562 | { | ||
563 | struct dma_device *dev = chan->device; | ||
564 | struct dma_async_tx_descriptor *tx; | ||
565 | dma_addr_t addr; | ||
566 | dma_cookie_t cookie; | ||
567 | int cpu; | ||
568 | |||
569 | tx = dev->device_prep_dma_memcpy(chan, len, 0); | ||
570 | if (!tx) | ||
571 | return -ENOMEM; | ||
572 | |||
573 | tx->ack = 1; | ||
574 | tx->callback = NULL; | ||
575 | addr = dma_map_page(dev->dev, src_pg, src_off, len, DMA_TO_DEVICE); | ||
576 | tx->tx_set_src(addr, tx, 0); | ||
577 | addr = dma_map_page(dev->dev, dest_pg, dest_off, len, DMA_FROM_DEVICE); | ||
578 | tx->tx_set_dest(addr, tx, 0); | ||
579 | cookie = tx->tx_submit(tx); | ||
580 | |||
581 | cpu = get_cpu(); | ||
582 | per_cpu_ptr(chan->local, cpu)->bytes_transferred += len; | ||
583 | per_cpu_ptr(chan->local, cpu)->memcpy_count++; | ||
584 | put_cpu(); | ||
585 | |||
586 | return cookie; | ||
587 | } | ||
588 | EXPORT_SYMBOL(dma_async_memcpy_pg_to_pg); | ||
589 | |||
590 | void dma_async_tx_descriptor_init(struct dma_async_tx_descriptor *tx, | ||
591 | struct dma_chan *chan) | ||
592 | { | ||
593 | tx->chan = chan; | ||
594 | spin_lock_init(&tx->lock); | ||
595 | INIT_LIST_HEAD(&tx->depend_node); | ||
596 | INIT_LIST_HEAD(&tx->depend_list); | ||
597 | } | ||
598 | EXPORT_SYMBOL(dma_async_tx_descriptor_init); | ||
599 | |||
418 | static int __init dma_bus_init(void) | 600 | static int __init dma_bus_init(void) |
419 | { | 601 | { |
420 | mutex_init(&dma_list_mutex); | 602 | mutex_init(&dma_list_mutex); |
diff --git a/drivers/dma/ioatdma.c b/drivers/dma/ioatdma.c index c4209af4fde4..171044930282 100644 --- a/drivers/dma/ioatdma.c +++ b/drivers/dma/ioatdma.c | |||
@@ -38,6 +38,7 @@ | |||
38 | #define to_ioat_chan(chan) container_of(chan, struct ioat_dma_chan, common) | 38 | #define to_ioat_chan(chan) container_of(chan, struct ioat_dma_chan, common) |
39 | #define to_ioat_device(dev) container_of(dev, struct ioat_device, common) | 39 | #define to_ioat_device(dev) container_of(dev, struct ioat_device, common) |
40 | #define to_ioat_desc(lh) container_of(lh, struct ioat_desc_sw, node) | 40 | #define to_ioat_desc(lh) container_of(lh, struct ioat_desc_sw, node) |
41 | #define tx_to_ioat_desc(tx) container_of(tx, struct ioat_desc_sw, async_tx) | ||
41 | 42 | ||
42 | /* internal functions */ | 43 | /* internal functions */ |
43 | static int __devinit ioat_probe(struct pci_dev *pdev, const struct pci_device_id *ent); | 44 | static int __devinit ioat_probe(struct pci_dev *pdev, const struct pci_device_id *ent); |
@@ -78,6 +79,73 @@ static int enumerate_dma_channels(struct ioat_device *device) | |||
78 | return device->common.chancnt; | 79 | return device->common.chancnt; |
79 | } | 80 | } |
80 | 81 | ||
82 | static void | ||
83 | ioat_set_src(dma_addr_t addr, struct dma_async_tx_descriptor *tx, int index) | ||
84 | { | ||
85 | struct ioat_desc_sw *iter, *desc = tx_to_ioat_desc(tx); | ||
86 | struct ioat_dma_chan *ioat_chan = to_ioat_chan(tx->chan); | ||
87 | |||
88 | pci_unmap_addr_set(desc, src, addr); | ||
89 | |||
90 | list_for_each_entry(iter, &desc->async_tx.tx_list, node) { | ||
91 | iter->hw->src_addr = addr; | ||
92 | addr += ioat_chan->xfercap; | ||
93 | } | ||
94 | |||
95 | } | ||
96 | |||
97 | static void | ||
98 | ioat_set_dest(dma_addr_t addr, struct dma_async_tx_descriptor *tx, int index) | ||
99 | { | ||
100 | struct ioat_desc_sw *iter, *desc = tx_to_ioat_desc(tx); | ||
101 | struct ioat_dma_chan *ioat_chan = to_ioat_chan(tx->chan); | ||
102 | |||
103 | pci_unmap_addr_set(desc, dst, addr); | ||
104 | |||
105 | list_for_each_entry(iter, &desc->async_tx.tx_list, node) { | ||
106 | iter->hw->dst_addr = addr; | ||
107 | addr += ioat_chan->xfercap; | ||
108 | } | ||
109 | } | ||
110 | |||
111 | static dma_cookie_t | ||
112 | ioat_tx_submit(struct dma_async_tx_descriptor *tx) | ||
113 | { | ||
114 | struct ioat_dma_chan *ioat_chan = to_ioat_chan(tx->chan); | ||
115 | struct ioat_desc_sw *desc = tx_to_ioat_desc(tx); | ||
116 | int append = 0; | ||
117 | dma_cookie_t cookie; | ||
118 | struct ioat_desc_sw *group_start; | ||
119 | |||
120 | group_start = list_entry(desc->async_tx.tx_list.next, | ||
121 | struct ioat_desc_sw, node); | ||
122 | spin_lock_bh(&ioat_chan->desc_lock); | ||
123 | /* cookie incr and addition to used_list must be atomic */ | ||
124 | cookie = ioat_chan->common.cookie; | ||
125 | cookie++; | ||
126 | if (cookie < 0) | ||
127 | cookie = 1; | ||
128 | ioat_chan->common.cookie = desc->async_tx.cookie = cookie; | ||
129 | |||
130 | /* write address into NextDescriptor field of last desc in chain */ | ||
131 | to_ioat_desc(ioat_chan->used_desc.prev)->hw->next = | ||
132 | group_start->async_tx.phys; | ||
133 | list_splice_init(&desc->async_tx.tx_list, ioat_chan->used_desc.prev); | ||
134 | |||
135 | ioat_chan->pending += desc->tx_cnt; | ||
136 | if (ioat_chan->pending >= 4) { | ||
137 | append = 1; | ||
138 | ioat_chan->pending = 0; | ||
139 | } | ||
140 | spin_unlock_bh(&ioat_chan->desc_lock); | ||
141 | |||
142 | if (append) | ||
143 | writeb(IOAT_CHANCMD_APPEND, | ||
144 | ioat_chan->reg_base + IOAT_CHANCMD_OFFSET); | ||
145 | |||
146 | return cookie; | ||
147 | } | ||
148 | |||
81 | static struct ioat_desc_sw *ioat_dma_alloc_descriptor( | 149 | static struct ioat_desc_sw *ioat_dma_alloc_descriptor( |
82 | struct ioat_dma_chan *ioat_chan, | 150 | struct ioat_dma_chan *ioat_chan, |
83 | gfp_t flags) | 151 | gfp_t flags) |
@@ -99,8 +167,13 @@ static struct ioat_desc_sw *ioat_dma_alloc_descriptor( | |||
99 | } | 167 | } |
100 | 168 | ||
101 | memset(desc, 0, sizeof(*desc)); | 169 | memset(desc, 0, sizeof(*desc)); |
170 | dma_async_tx_descriptor_init(&desc_sw->async_tx, &ioat_chan->common); | ||
171 | desc_sw->async_tx.tx_set_src = ioat_set_src; | ||
172 | desc_sw->async_tx.tx_set_dest = ioat_set_dest; | ||
173 | desc_sw->async_tx.tx_submit = ioat_tx_submit; | ||
174 | INIT_LIST_HEAD(&desc_sw->async_tx.tx_list); | ||
102 | desc_sw->hw = desc; | 175 | desc_sw->hw = desc; |
103 | desc_sw->phys = phys; | 176 | desc_sw->async_tx.phys = phys; |
104 | 177 | ||
105 | return desc_sw; | 178 | return desc_sw; |
106 | } | 179 | } |
@@ -188,12 +261,14 @@ static void ioat_dma_free_chan_resources(struct dma_chan *chan) | |||
188 | list_for_each_entry_safe(desc, _desc, &ioat_chan->used_desc, node) { | 261 | list_for_each_entry_safe(desc, _desc, &ioat_chan->used_desc, node) { |
189 | in_use_descs++; | 262 | in_use_descs++; |
190 | list_del(&desc->node); | 263 | list_del(&desc->node); |
191 | pci_pool_free(ioat_device->dma_pool, desc->hw, desc->phys); | 264 | pci_pool_free(ioat_device->dma_pool, desc->hw, |
265 | desc->async_tx.phys); | ||
192 | kfree(desc); | 266 | kfree(desc); |
193 | } | 267 | } |
194 | list_for_each_entry_safe(desc, _desc, &ioat_chan->free_desc, node) { | 268 | list_for_each_entry_safe(desc, _desc, &ioat_chan->free_desc, node) { |
195 | list_del(&desc->node); | 269 | list_del(&desc->node); |
196 | pci_pool_free(ioat_device->dma_pool, desc->hw, desc->phys); | 270 | pci_pool_free(ioat_device->dma_pool, desc->hw, |
271 | desc->async_tx.phys); | ||
197 | kfree(desc); | 272 | kfree(desc); |
198 | } | 273 | } |
199 | spin_unlock_bh(&ioat_chan->desc_lock); | 274 | spin_unlock_bh(&ioat_chan->desc_lock); |
@@ -215,45 +290,25 @@ static void ioat_dma_free_chan_resources(struct dma_chan *chan) | |||
215 | writew(chanctrl, ioat_chan->reg_base + IOAT_CHANCTRL_OFFSET); | 290 | writew(chanctrl, ioat_chan->reg_base + IOAT_CHANCTRL_OFFSET); |
216 | } | 291 | } |
217 | 292 | ||
218 | /** | 293 | static struct dma_async_tx_descriptor * |
219 | * do_ioat_dma_memcpy - actual function that initiates a IOAT DMA transaction | 294 | ioat_dma_prep_memcpy(struct dma_chan *chan, size_t len, int int_en) |
220 | * @ioat_chan: IOAT DMA channel handle | ||
221 | * @dest: DMA destination address | ||
222 | * @src: DMA source address | ||
223 | * @len: transaction length in bytes | ||
224 | */ | ||
225 | |||
226 | static dma_cookie_t do_ioat_dma_memcpy(struct ioat_dma_chan *ioat_chan, | ||
227 | dma_addr_t dest, | ||
228 | dma_addr_t src, | ||
229 | size_t len) | ||
230 | { | 295 | { |
231 | struct ioat_desc_sw *first; | 296 | struct ioat_dma_chan *ioat_chan = to_ioat_chan(chan); |
232 | struct ioat_desc_sw *prev; | 297 | struct ioat_desc_sw *first, *prev, *new; |
233 | struct ioat_desc_sw *new; | ||
234 | dma_cookie_t cookie; | ||
235 | LIST_HEAD(new_chain); | 298 | LIST_HEAD(new_chain); |
236 | u32 copy; | 299 | u32 copy; |
237 | size_t orig_len; | 300 | size_t orig_len; |
238 | dma_addr_t orig_src, orig_dst; | 301 | int desc_count = 0; |
239 | unsigned int desc_count = 0; | ||
240 | unsigned int append = 0; | ||
241 | |||
242 | if (!ioat_chan || !dest || !src) | ||
243 | return -EFAULT; | ||
244 | 302 | ||
245 | if (!len) | 303 | if (!len) |
246 | return ioat_chan->common.cookie; | 304 | return NULL; |
247 | 305 | ||
248 | orig_len = len; | 306 | orig_len = len; |
249 | orig_src = src; | ||
250 | orig_dst = dest; | ||
251 | 307 | ||
252 | first = NULL; | 308 | first = NULL; |
253 | prev = NULL; | 309 | prev = NULL; |
254 | 310 | ||
255 | spin_lock_bh(&ioat_chan->desc_lock); | 311 | spin_lock_bh(&ioat_chan->desc_lock); |
256 | |||
257 | while (len) { | 312 | while (len) { |
258 | if (!list_empty(&ioat_chan->free_desc)) { | 313 | if (!list_empty(&ioat_chan->free_desc)) { |
259 | new = to_ioat_desc(ioat_chan->free_desc.next); | 314 | new = to_ioat_desc(ioat_chan->free_desc.next); |
@@ -270,140 +325,36 @@ static dma_cookie_t do_ioat_dma_memcpy(struct ioat_dma_chan *ioat_chan, | |||
270 | 325 | ||
271 | new->hw->size = copy; | 326 | new->hw->size = copy; |
272 | new->hw->ctl = 0; | 327 | new->hw->ctl = 0; |
273 | new->hw->src_addr = src; | 328 | new->async_tx.cookie = 0; |
274 | new->hw->dst_addr = dest; | 329 | new->async_tx.ack = 1; |
275 | new->cookie = 0; | ||
276 | 330 | ||
277 | /* chain together the physical address list for the HW */ | 331 | /* chain together the physical address list for the HW */ |
278 | if (!first) | 332 | if (!first) |
279 | first = new; | 333 | first = new; |
280 | else | 334 | else |
281 | prev->hw->next = (u64) new->phys; | 335 | prev->hw->next = (u64) new->async_tx.phys; |
282 | 336 | ||
283 | prev = new; | 337 | prev = new; |
284 | |||
285 | len -= copy; | 338 | len -= copy; |
286 | dest += copy; | ||
287 | src += copy; | ||
288 | |||
289 | list_add_tail(&new->node, &new_chain); | 339 | list_add_tail(&new->node, &new_chain); |
290 | desc_count++; | 340 | desc_count++; |
291 | } | 341 | } |
292 | new->hw->ctl = IOAT_DMA_DESCRIPTOR_CTL_CP_STS; | ||
293 | new->hw->next = 0; | ||
294 | 342 | ||
295 | /* cookie incr and addition to used_list must be atomic */ | 343 | list_splice(&new_chain, &new->async_tx.tx_list); |
296 | 344 | ||
297 | cookie = ioat_chan->common.cookie; | 345 | new->hw->ctl = IOAT_DMA_DESCRIPTOR_CTL_CP_STS; |
298 | cookie++; | 346 | new->hw->next = 0; |
299 | if (cookie < 0) | 347 | new->tx_cnt = desc_count; |
300 | cookie = 1; | 348 | new->async_tx.ack = 0; /* client is in control of this ack */ |
301 | ioat_chan->common.cookie = new->cookie = cookie; | 349 | new->async_tx.cookie = -EBUSY; |
302 | 350 | ||
303 | pci_unmap_addr_set(new, src, orig_src); | ||
304 | pci_unmap_addr_set(new, dst, orig_dst); | ||
305 | pci_unmap_len_set(new, src_len, orig_len); | 351 | pci_unmap_len_set(new, src_len, orig_len); |
306 | pci_unmap_len_set(new, dst_len, orig_len); | 352 | pci_unmap_len_set(new, dst_len, orig_len); |
307 | |||
308 | /* write address into NextDescriptor field of last desc in chain */ | ||
309 | to_ioat_desc(ioat_chan->used_desc.prev)->hw->next = first->phys; | ||
310 | list_splice_init(&new_chain, ioat_chan->used_desc.prev); | ||
311 | |||
312 | ioat_chan->pending += desc_count; | ||
313 | if (ioat_chan->pending >= 4) { | ||
314 | append = 1; | ||
315 | ioat_chan->pending = 0; | ||
316 | } | ||
317 | |||
318 | spin_unlock_bh(&ioat_chan->desc_lock); | 353 | spin_unlock_bh(&ioat_chan->desc_lock); |
319 | 354 | ||
320 | if (append) | 355 | return new ? &new->async_tx : NULL; |
321 | writeb(IOAT_CHANCMD_APPEND, | ||
322 | ioat_chan->reg_base + IOAT_CHANCMD_OFFSET); | ||
323 | return cookie; | ||
324 | } | 356 | } |
325 | 357 | ||
326 | /** | ||
327 | * ioat_dma_memcpy_buf_to_buf - wrapper that takes src & dest bufs | ||
328 | * @chan: IOAT DMA channel handle | ||
329 | * @dest: DMA destination address | ||
330 | * @src: DMA source address | ||
331 | * @len: transaction length in bytes | ||
332 | */ | ||
333 | |||
334 | static dma_cookie_t ioat_dma_memcpy_buf_to_buf(struct dma_chan *chan, | ||
335 | void *dest, | ||
336 | void *src, | ||
337 | size_t len) | ||
338 | { | ||
339 | dma_addr_t dest_addr; | ||
340 | dma_addr_t src_addr; | ||
341 | struct ioat_dma_chan *ioat_chan = to_ioat_chan(chan); | ||
342 | |||
343 | dest_addr = pci_map_single(ioat_chan->device->pdev, | ||
344 | dest, len, PCI_DMA_FROMDEVICE); | ||
345 | src_addr = pci_map_single(ioat_chan->device->pdev, | ||
346 | src, len, PCI_DMA_TODEVICE); | ||
347 | |||
348 | return do_ioat_dma_memcpy(ioat_chan, dest_addr, src_addr, len); | ||
349 | } | ||
350 | |||
351 | /** | ||
352 | * ioat_dma_memcpy_buf_to_pg - wrapper, copying from a buf to a page | ||
353 | * @chan: IOAT DMA channel handle | ||
354 | * @page: pointer to the page to copy to | ||
355 | * @offset: offset into that page | ||
356 | * @src: DMA source address | ||
357 | * @len: transaction length in bytes | ||
358 | */ | ||
359 | |||
360 | static dma_cookie_t ioat_dma_memcpy_buf_to_pg(struct dma_chan *chan, | ||
361 | struct page *page, | ||
362 | unsigned int offset, | ||
363 | void *src, | ||
364 | size_t len) | ||
365 | { | ||
366 | dma_addr_t dest_addr; | ||
367 | dma_addr_t src_addr; | ||
368 | struct ioat_dma_chan *ioat_chan = to_ioat_chan(chan); | ||
369 | |||
370 | dest_addr = pci_map_page(ioat_chan->device->pdev, | ||
371 | page, offset, len, PCI_DMA_FROMDEVICE); | ||
372 | src_addr = pci_map_single(ioat_chan->device->pdev, | ||
373 | src, len, PCI_DMA_TODEVICE); | ||
374 | |||
375 | return do_ioat_dma_memcpy(ioat_chan, dest_addr, src_addr, len); | ||
376 | } | ||
377 | |||
378 | /** | ||
379 | * ioat_dma_memcpy_pg_to_pg - wrapper, copying between two pages | ||
380 | * @chan: IOAT DMA channel handle | ||
381 | * @dest_pg: pointer to the page to copy to | ||
382 | * @dest_off: offset into that page | ||
383 | * @src_pg: pointer to the page to copy from | ||
384 | * @src_off: offset into that page | ||
385 | * @len: transaction length in bytes. This is guaranteed not to make a copy | ||
386 | * across a page boundary. | ||
387 | */ | ||
388 | |||
389 | static dma_cookie_t ioat_dma_memcpy_pg_to_pg(struct dma_chan *chan, | ||
390 | struct page *dest_pg, | ||
391 | unsigned int dest_off, | ||
392 | struct page *src_pg, | ||
393 | unsigned int src_off, | ||
394 | size_t len) | ||
395 | { | ||
396 | dma_addr_t dest_addr; | ||
397 | dma_addr_t src_addr; | ||
398 | struct ioat_dma_chan *ioat_chan = to_ioat_chan(chan); | ||
399 | |||
400 | dest_addr = pci_map_page(ioat_chan->device->pdev, | ||
401 | dest_pg, dest_off, len, PCI_DMA_FROMDEVICE); | ||
402 | src_addr = pci_map_page(ioat_chan->device->pdev, | ||
403 | src_pg, src_off, len, PCI_DMA_TODEVICE); | ||
404 | |||
405 | return do_ioat_dma_memcpy(ioat_chan, dest_addr, src_addr, len); | ||
406 | } | ||
407 | 358 | ||
408 | /** | 359 | /** |
409 | * ioat_dma_memcpy_issue_pending - push potentially unrecognized appended descriptors to hw | 360 | * ioat_dma_memcpy_issue_pending - push potentially unrecognized appended descriptors to hw |
@@ -465,8 +416,8 @@ static void ioat_dma_memcpy_cleanup(struct ioat_dma_chan *chan) | |||
465 | * exceeding xfercap, perhaps. If so, only the last one will | 416 | * exceeding xfercap, perhaps. If so, only the last one will |
466 | * have a cookie, and require unmapping. | 417 | * have a cookie, and require unmapping. |
467 | */ | 418 | */ |
468 | if (desc->cookie) { | 419 | if (desc->async_tx.cookie) { |
469 | cookie = desc->cookie; | 420 | cookie = desc->async_tx.cookie; |
470 | 421 | ||
471 | /* yes we are unmapping both _page and _single alloc'd | 422 | /* yes we are unmapping both _page and _single alloc'd |
472 | regions with unmap_page. Is this *really* that bad? | 423 | regions with unmap_page. Is this *really* that bad? |
@@ -481,14 +432,19 @@ static void ioat_dma_memcpy_cleanup(struct ioat_dma_chan *chan) | |||
481 | PCI_DMA_TODEVICE); | 432 | PCI_DMA_TODEVICE); |
482 | } | 433 | } |
483 | 434 | ||
484 | if (desc->phys != phys_complete) { | 435 | if (desc->async_tx.phys != phys_complete) { |
485 | /* a completed entry, but not the last, so cleanup */ | 436 | /* a completed entry, but not the last, so cleanup |
486 | list_del(&desc->node); | 437 | * if the client is done with the descriptor |
487 | list_add_tail(&desc->node, &chan->free_desc); | 438 | */ |
439 | if (desc->async_tx.ack) { | ||
440 | list_del(&desc->node); | ||
441 | list_add_tail(&desc->node, &chan->free_desc); | ||
442 | } else | ||
443 | desc->async_tx.cookie = 0; | ||
488 | } else { | 444 | } else { |
489 | /* last used desc. Do not remove, so we can append from | 445 | /* last used desc. Do not remove, so we can append from |
490 | it, but don't look at it next time, either */ | 446 | it, but don't look at it next time, either */ |
491 | desc->cookie = 0; | 447 | desc->async_tx.cookie = 0; |
492 | 448 | ||
493 | /* TODO check status bits? */ | 449 | /* TODO check status bits? */ |
494 | break; | 450 | break; |
@@ -504,6 +460,17 @@ static void ioat_dma_memcpy_cleanup(struct ioat_dma_chan *chan) | |||
504 | spin_unlock(&chan->cleanup_lock); | 460 | spin_unlock(&chan->cleanup_lock); |
505 | } | 461 | } |
506 | 462 | ||
463 | static void ioat_dma_dependency_added(struct dma_chan *chan) | ||
464 | { | ||
465 | struct ioat_dma_chan *ioat_chan = to_ioat_chan(chan); | ||
466 | spin_lock_bh(&ioat_chan->desc_lock); | ||
467 | if (ioat_chan->pending == 0) { | ||
468 | spin_unlock_bh(&ioat_chan->desc_lock); | ||
469 | ioat_dma_memcpy_cleanup(ioat_chan); | ||
470 | } else | ||
471 | spin_unlock_bh(&ioat_chan->desc_lock); | ||
472 | } | ||
473 | |||
507 | /** | 474 | /** |
508 | * ioat_dma_is_complete - poll the status of a IOAT DMA transaction | 475 | * ioat_dma_is_complete - poll the status of a IOAT DMA transaction |
509 | * @chan: IOAT DMA channel handle | 476 | * @chan: IOAT DMA channel handle |
@@ -606,13 +573,14 @@ static void ioat_start_null_desc(struct ioat_dma_chan *ioat_chan) | |||
606 | 573 | ||
607 | desc->hw->ctl = IOAT_DMA_DESCRIPTOR_NUL; | 574 | desc->hw->ctl = IOAT_DMA_DESCRIPTOR_NUL; |
608 | desc->hw->next = 0; | 575 | desc->hw->next = 0; |
576 | desc->async_tx.ack = 1; | ||
609 | 577 | ||
610 | list_add_tail(&desc->node, &ioat_chan->used_desc); | 578 | list_add_tail(&desc->node, &ioat_chan->used_desc); |
611 | spin_unlock_bh(&ioat_chan->desc_lock); | 579 | spin_unlock_bh(&ioat_chan->desc_lock); |
612 | 580 | ||
613 | writel(((u64) desc->phys) & 0x00000000FFFFFFFF, | 581 | writel(((u64) desc->async_tx.phys) & 0x00000000FFFFFFFF, |
614 | ioat_chan->reg_base + IOAT_CHAINADDR_OFFSET_LOW); | 582 | ioat_chan->reg_base + IOAT_CHAINADDR_OFFSET_LOW); |
615 | writel(((u64) desc->phys) >> 32, | 583 | writel(((u64) desc->async_tx.phys) >> 32, |
616 | ioat_chan->reg_base + IOAT_CHAINADDR_OFFSET_HIGH); | 584 | ioat_chan->reg_base + IOAT_CHAINADDR_OFFSET_HIGH); |
617 | 585 | ||
618 | writeb(IOAT_CHANCMD_START, ioat_chan->reg_base + IOAT_CHANCMD_OFFSET); | 586 | writeb(IOAT_CHANCMD_START, ioat_chan->reg_base + IOAT_CHANCMD_OFFSET); |
@@ -629,6 +597,8 @@ static int ioat_self_test(struct ioat_device *device) | |||
629 | u8 *src; | 597 | u8 *src; |
630 | u8 *dest; | 598 | u8 *dest; |
631 | struct dma_chan *dma_chan; | 599 | struct dma_chan *dma_chan; |
600 | struct dma_async_tx_descriptor *tx; | ||
601 | dma_addr_t addr; | ||
632 | dma_cookie_t cookie; | 602 | dma_cookie_t cookie; |
633 | int err = 0; | 603 | int err = 0; |
634 | 604 | ||
@@ -654,7 +624,15 @@ static int ioat_self_test(struct ioat_device *device) | |||
654 | goto out; | 624 | goto out; |
655 | } | 625 | } |
656 | 626 | ||
657 | cookie = ioat_dma_memcpy_buf_to_buf(dma_chan, dest, src, IOAT_TEST_SIZE); | 627 | tx = ioat_dma_prep_memcpy(dma_chan, IOAT_TEST_SIZE, 0); |
628 | async_tx_ack(tx); | ||
629 | addr = dma_map_single(dma_chan->device->dev, src, IOAT_TEST_SIZE, | ||
630 | DMA_TO_DEVICE); | ||
631 | ioat_set_src(addr, tx, 0); | ||
632 | addr = dma_map_single(dma_chan->device->dev, dest, IOAT_TEST_SIZE, | ||
633 | DMA_FROM_DEVICE); | ||
634 | ioat_set_dest(addr, tx, 0); | ||
635 | cookie = ioat_tx_submit(tx); | ||
658 | ioat_dma_memcpy_issue_pending(dma_chan); | 636 | ioat_dma_memcpy_issue_pending(dma_chan); |
659 | msleep(1); | 637 | msleep(1); |
660 | 638 | ||
@@ -750,13 +728,14 @@ static int __devinit ioat_probe(struct pci_dev *pdev, | |||
750 | INIT_LIST_HEAD(&device->common.channels); | 728 | INIT_LIST_HEAD(&device->common.channels); |
751 | enumerate_dma_channels(device); | 729 | enumerate_dma_channels(device); |
752 | 730 | ||
731 | dma_cap_set(DMA_MEMCPY, device->common.cap_mask); | ||
753 | device->common.device_alloc_chan_resources = ioat_dma_alloc_chan_resources; | 732 | device->common.device_alloc_chan_resources = ioat_dma_alloc_chan_resources; |
754 | device->common.device_free_chan_resources = ioat_dma_free_chan_resources; | 733 | device->common.device_free_chan_resources = ioat_dma_free_chan_resources; |
755 | device->common.device_memcpy_buf_to_buf = ioat_dma_memcpy_buf_to_buf; | 734 | device->common.device_prep_dma_memcpy = ioat_dma_prep_memcpy; |
756 | device->common.device_memcpy_buf_to_pg = ioat_dma_memcpy_buf_to_pg; | 735 | device->common.device_is_tx_complete = ioat_dma_is_complete; |
757 | device->common.device_memcpy_pg_to_pg = ioat_dma_memcpy_pg_to_pg; | 736 | device->common.device_issue_pending = ioat_dma_memcpy_issue_pending; |
758 | device->common.device_memcpy_complete = ioat_dma_is_complete; | 737 | device->common.device_dependency_added = ioat_dma_dependency_added; |
759 | device->common.device_memcpy_issue_pending = ioat_dma_memcpy_issue_pending; | 738 | device->common.dev = &pdev->dev; |
760 | printk(KERN_INFO "Intel(R) I/OAT DMA Engine found, %d channels\n", | 739 | printk(KERN_INFO "Intel(R) I/OAT DMA Engine found, %d channels\n", |
761 | device->common.chancnt); | 740 | device->common.chancnt); |
762 | 741 | ||
diff --git a/drivers/dma/ioatdma.h b/drivers/dma/ioatdma.h index 62b26a9be4c9..d3f69bb15fa0 100644 --- a/drivers/dma/ioatdma.h +++ b/drivers/dma/ioatdma.h | |||
@@ -105,21 +105,20 @@ struct ioat_dma_chan { | |||
105 | /** | 105 | /** |
106 | * struct ioat_desc_sw - wrapper around hardware descriptor | 106 | * struct ioat_desc_sw - wrapper around hardware descriptor |
107 | * @hw: hardware DMA descriptor | 107 | * @hw: hardware DMA descriptor |
108 | * @node: | 108 | * @node: this descriptor will either be on the free list, |
109 | * @cookie: | 109 | * or attached to a transaction list (async_tx.tx_list) |
110 | * @phys: | 110 | * @tx_cnt: number of descriptors required to complete the transaction |
111 | * @async_tx: the generic software descriptor for all engines | ||
111 | */ | 112 | */ |
112 | |||
113 | struct ioat_desc_sw { | 113 | struct ioat_desc_sw { |
114 | struct ioat_dma_descriptor *hw; | 114 | struct ioat_dma_descriptor *hw; |
115 | struct list_head node; | 115 | struct list_head node; |
116 | dma_cookie_t cookie; | 116 | int tx_cnt; |
117 | dma_addr_t phys; | ||
118 | DECLARE_PCI_UNMAP_ADDR(src) | 117 | DECLARE_PCI_UNMAP_ADDR(src) |
119 | DECLARE_PCI_UNMAP_LEN(src_len) | 118 | DECLARE_PCI_UNMAP_LEN(src_len) |
120 | DECLARE_PCI_UNMAP_ADDR(dst) | 119 | DECLARE_PCI_UNMAP_ADDR(dst) |
121 | DECLARE_PCI_UNMAP_LEN(dst_len) | 120 | DECLARE_PCI_UNMAP_LEN(dst_len) |
121 | struct dma_async_tx_descriptor async_tx; | ||
122 | }; | 122 | }; |
123 | 123 | ||
124 | #endif /* IOATDMA_H */ | 124 | #endif /* IOATDMA_H */ |
125 | |||