diff options
author | Dan Williams <dan.j.williams@intel.com> | 2009-07-28 17:44:04 -0400 |
---|---|---|
committer | Dan Williams <dan.j.williams@intel.com> | 2009-09-08 20:29:55 -0400 |
commit | a0587bcf3e64029a4da2a5666cad18df38db0d56 (patch) | |
tree | 475b3a2a7cd102f40d7c16fed431c227576c255a /drivers/dma | |
parent | c7984f4e4e3af3bf8027d636283ea8658c7f80b9 (diff) |
ioat1: move descriptor allocation from submit to prep
The async_tx api assumes that after a successful ->prep a subsequent
->submit will not fail due to a lack of resources.
This also fixes a bug in the allocation failure case. Previously the
descriptors allocated prior to the allocation failure would not be
returned to the free list.
Signed-off-by: Maciej Sosnowski <maciej.sosnowski@intel.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Diffstat (limited to 'drivers/dma')
-rw-r--r-- | drivers/dma/ioat/dma.c | 154 |
1 files changed, 65 insertions, 89 deletions
diff --git a/drivers/dma/ioat/dma.c b/drivers/dma/ioat/dma.c index 4840d4805d8c..c4333be07608 100644 --- a/drivers/dma/ioat/dma.c +++ b/drivers/dma/ioat/dma.c | |||
@@ -420,95 +420,29 @@ static void ioat_dma_chan_watchdog(struct work_struct *work) | |||
420 | static dma_cookie_t ioat1_tx_submit(struct dma_async_tx_descriptor *tx) | 420 | static dma_cookie_t ioat1_tx_submit(struct dma_async_tx_descriptor *tx) |
421 | { | 421 | { |
422 | struct ioat_dma_chan *ioat_chan = to_ioat_chan(tx->chan); | 422 | struct ioat_dma_chan *ioat_chan = to_ioat_chan(tx->chan); |
423 | struct ioat_desc_sw *first = tx_to_ioat_desc(tx); | 423 | struct ioat_desc_sw *desc = tx_to_ioat_desc(tx); |
424 | struct ioat_desc_sw *prev, *new; | 424 | struct ioat_desc_sw *first; |
425 | struct ioat_dma_descriptor *hw; | 425 | struct ioat_desc_sw *chain_tail; |
426 | dma_cookie_t cookie; | 426 | dma_cookie_t cookie; |
427 | LIST_HEAD(new_chain); | ||
428 | u32 copy; | ||
429 | size_t len; | ||
430 | dma_addr_t src, dst; | ||
431 | unsigned long orig_flags; | ||
432 | unsigned int desc_count = 0; | ||
433 | |||
434 | /* src and dest and len are stored in the initial descriptor */ | ||
435 | len = first->len; | ||
436 | src = first->src; | ||
437 | dst = first->dst; | ||
438 | orig_flags = first->txd.flags; | ||
439 | new = first; | ||
440 | 427 | ||
441 | spin_lock_bh(&ioat_chan->desc_lock); | 428 | spin_lock_bh(&ioat_chan->desc_lock); |
442 | prev = to_ioat_desc(ioat_chan->used_desc.prev); | ||
443 | prefetch(prev->hw); | ||
444 | do { | ||
445 | copy = min_t(size_t, len, ioat_chan->xfercap); | ||
446 | |||
447 | async_tx_ack(&new->txd); | ||
448 | |||
449 | hw = new->hw; | ||
450 | hw->size = copy; | ||
451 | hw->ctl = 0; | ||
452 | hw->src_addr = src; | ||
453 | hw->dst_addr = dst; | ||
454 | hw->next = 0; | ||
455 | |||
456 | /* chain together the physical address list for the HW */ | ||
457 | wmb(); | ||
458 | prev->hw->next = (u64) new->txd.phys; | ||
459 | |||
460 | len -= copy; | ||
461 | dst += copy; | ||
462 | src += copy; | ||
463 | |||
464 | list_add_tail(&new->node, &new_chain); | ||
465 | desc_count++; | ||
466 | prev = new; | ||
467 | } while (len && (new = ioat1_dma_get_next_descriptor(ioat_chan))); | ||
468 | |||
469 | if (!new) { | ||
470 | dev_err(to_dev(ioat_chan), "tx submit failed\n"); | ||
471 | spin_unlock_bh(&ioat_chan->desc_lock); | ||
472 | return -ENOMEM; | ||
473 | } | ||
474 | |||
475 | hw->ctl_f.compl_write = 1; | ||
476 | if (first->txd.callback) { | ||
477 | hw->ctl_f.int_en = 1; | ||
478 | if (first != new) { | ||
479 | /* move callback into to last desc */ | ||
480 | new->txd.callback = first->txd.callback; | ||
481 | new->txd.callback_param | ||
482 | = first->txd.callback_param; | ||
483 | first->txd.callback = NULL; | ||
484 | first->txd.callback_param = NULL; | ||
485 | } | ||
486 | } | ||
487 | |||
488 | new->tx_cnt = desc_count; | ||
489 | new->txd.flags = orig_flags; /* client is in control of this ack */ | ||
490 | |||
491 | /* store the original values for use in later cleanup */ | ||
492 | if (new != first) { | ||
493 | new->src = first->src; | ||
494 | new->dst = first->dst; | ||
495 | new->len = first->len; | ||
496 | } | ||
497 | |||
498 | /* cookie incr and addition to used_list must be atomic */ | 429 | /* cookie incr and addition to used_list must be atomic */ |
499 | cookie = ioat_chan->common.cookie; | 430 | cookie = ioat_chan->common.cookie; |
500 | cookie++; | 431 | cookie++; |
501 | if (cookie < 0) | 432 | if (cookie < 0) |
502 | cookie = 1; | 433 | cookie = 1; |
503 | ioat_chan->common.cookie = new->txd.cookie = cookie; | 434 | ioat_chan->common.cookie = tx->cookie = cookie; |
504 | 435 | ||
505 | /* write address into NextDescriptor field of last desc in chain */ | 436 | /* write address into NextDescriptor field of last desc in chain */ |
506 | to_ioat_desc(ioat_chan->used_desc.prev)->hw->next = | 437 | first = to_ioat_desc(tx->tx_list.next); |
507 | first->txd.phys; | 438 | chain_tail = to_ioat_desc(ioat_chan->used_desc.prev); |
508 | list_splice_tail(&new_chain, &ioat_chan->used_desc); | 439 | /* make descriptor updates globally visible before chaining */ |
509 | 440 | wmb(); | |
510 | ioat_chan->dmacount += desc_count; | 441 | chain_tail->hw->next = first->txd.phys; |
511 | ioat_chan->pending += desc_count; | 442 | list_splice_tail_init(&tx->tx_list, &ioat_chan->used_desc); |
443 | |||
444 | ioat_chan->dmacount += desc->tx_cnt; | ||
445 | ioat_chan->pending += desc->tx_cnt; | ||
512 | if (ioat_chan->pending >= ioat_pending_level) | 446 | if (ioat_chan->pending >= ioat_pending_level) |
513 | __ioat1_dma_memcpy_issue_pending(ioat_chan); | 447 | __ioat1_dma_memcpy_issue_pending(ioat_chan); |
514 | spin_unlock_bh(&ioat_chan->desc_lock); | 448 | spin_unlock_bh(&ioat_chan->desc_lock); |
@@ -937,24 +871,66 @@ ioat1_dma_prep_memcpy(struct dma_chan *chan, dma_addr_t dma_dest, | |||
937 | dma_addr_t dma_src, size_t len, unsigned long flags) | 871 | dma_addr_t dma_src, size_t len, unsigned long flags) |
938 | { | 872 | { |
939 | struct ioat_dma_chan *ioat_chan = to_ioat_chan(chan); | 873 | struct ioat_dma_chan *ioat_chan = to_ioat_chan(chan); |
940 | struct ioat_desc_sw *new; | 874 | struct ioat_desc_sw *desc; |
875 | size_t copy; | ||
876 | LIST_HEAD(chain); | ||
877 | dma_addr_t src = dma_src; | ||
878 | dma_addr_t dest = dma_dest; | ||
879 | size_t total_len = len; | ||
880 | struct ioat_dma_descriptor *hw = NULL; | ||
881 | int tx_cnt = 0; | ||
941 | 882 | ||
942 | spin_lock_bh(&ioat_chan->desc_lock); | 883 | spin_lock_bh(&ioat_chan->desc_lock); |
943 | new = ioat_dma_get_next_descriptor(ioat_chan); | 884 | desc = ioat_dma_get_next_descriptor(ioat_chan); |
944 | spin_unlock_bh(&ioat_chan->desc_lock); | 885 | do { |
886 | if (!desc) | ||
887 | break; | ||
945 | 888 | ||
946 | if (new) { | 889 | tx_cnt++; |
947 | new->len = len; | 890 | copy = min_t(size_t, len, ioat_chan->xfercap); |
948 | new->dst = dma_dest; | 891 | |
949 | new->src = dma_src; | 892 | hw = desc->hw; |
950 | new->txd.flags = flags; | 893 | hw->size = copy; |
951 | return &new->txd; | 894 | hw->ctl = 0; |
952 | } else { | 895 | hw->src_addr = src; |
896 | hw->dst_addr = dest; | ||
897 | |||
898 | list_add_tail(&desc->node, &chain); | ||
899 | |||
900 | len -= copy; | ||
901 | dest += copy; | ||
902 | src += copy; | ||
903 | if (len) { | ||
904 | struct ioat_desc_sw *next; | ||
905 | |||
906 | async_tx_ack(&desc->txd); | ||
907 | next = ioat_dma_get_next_descriptor(ioat_chan); | ||
908 | hw->next = next ? next->txd.phys : 0; | ||
909 | desc = next; | ||
910 | } else | ||
911 | hw->next = 0; | ||
912 | } while (len); | ||
913 | |||
914 | if (!desc) { | ||
953 | dev_err(to_dev(ioat_chan), | 915 | dev_err(to_dev(ioat_chan), |
954 | "chan%d - get_next_desc failed: %d descs waiting, %d total desc\n", | 916 | "chan%d - get_next_desc failed: %d descs waiting, %d total desc\n", |
955 | chan_num(ioat_chan), ioat_chan->dmacount, ioat_chan->desccount); | 917 | chan_num(ioat_chan), ioat_chan->dmacount, ioat_chan->desccount); |
918 | list_splice(&chain, &ioat_chan->free_desc); | ||
919 | spin_unlock_bh(&ioat_chan->desc_lock); | ||
956 | return NULL; | 920 | return NULL; |
957 | } | 921 | } |
922 | spin_unlock_bh(&ioat_chan->desc_lock); | ||
923 | |||
924 | desc->txd.flags = flags; | ||
925 | desc->tx_cnt = tx_cnt; | ||
926 | desc->src = dma_src; | ||
927 | desc->dst = dma_dest; | ||
928 | desc->len = total_len; | ||
929 | list_splice(&chain, &desc->txd.tx_list); | ||
930 | hw->ctl_f.int_en = !!(flags & DMA_PREP_INTERRUPT); | ||
931 | hw->ctl_f.compl_write = 1; | ||
932 | |||
933 | return &desc->txd; | ||
958 | } | 934 | } |
959 | 935 | ||
960 | static struct dma_async_tx_descriptor * | 936 | static struct dma_async_tx_descriptor * |