Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

dmaengine: mv_xor: improve descriptors list handling and reduce locking

This patch change the way free descriptors are marked.

Instead of having a field for descriptor in use, all the descriptors in the
all_slots list are free for use.

This simplify the allocation method and reduce the locking needed.

Signed-off-by: Lior Amsalem <alior@marvell.com>
Reviewed-by: Ofer Heifetz <oferh@marvell.com>
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>

authored by

Lior Amsalem and committed by
Vinod Koul
fbea28a2 f1d25e0a

+51 -103
+45 -92
drivers/dma/mv_xor.c
··· 88 88 hw_desc->phy_next_desc = next_desc_addr; 89 89 } 90 90 91 - static void mv_desc_clear_next_desc(struct mv_xor_desc_slot *desc) 92 - { 93 - struct mv_xor_desc *hw_desc = desc->hw_desc; 94 - hw_desc->phy_next_desc = 0; 95 - } 96 - 97 91 static void mv_desc_set_src_addr(struct mv_xor_desc_slot *desc, 98 92 int index, dma_addr_t addr) 99 93 { ··· 207 213 return (state == 1) ? 1 : 0; 208 214 } 209 215 210 - /** 211 - * mv_chan_free_slots - flags descriptor slots for reuse 212 - * @slot: Slot to free 213 - * Caller must hold &mv_chan->lock while calling this function 214 - */ 215 - static void mv_chan_free_slots(struct mv_xor_chan *mv_chan, 216 - struct mv_xor_desc_slot *slot) 217 - { 218 - dev_dbg(mv_chan_to_devp(mv_chan), "%s %d slot %p\n", 219 - __func__, __LINE__, slot); 220 - 221 - slot->slot_used = 0; 222 - 223 - } 224 - 225 216 /* 226 217 * mv_chan_start_new_chain - program the engine to operate on new 227 218 * chain headed by sw_desc ··· 258 279 259 280 dev_dbg(mv_chan_to_devp(mv_chan), "%s %d\n", __func__, __LINE__); 260 281 list_for_each_entry_safe(iter, _iter, &mv_chan->completed_slots, 261 - completed_node) { 282 + node) { 262 283 263 - if (async_tx_test_ack(&iter->async_tx)) { 264 - list_del(&iter->completed_node); 265 - mv_chan_free_slots(mv_chan, iter); 266 - } 284 + if (async_tx_test_ack(&iter->async_tx)) 285 + list_move_tail(&iter->node, &mv_chan->free_slots); 267 286 } 268 287 return 0; 269 288 } ··· 272 295 { 273 296 dev_dbg(mv_chan_to_devp(mv_chan), "%s %d: desc %p flags %d\n", 274 297 __func__, __LINE__, desc, desc->async_tx.flags); 275 - list_del(&desc->chain_node); 298 + 276 299 /* the client is allowed to attach dependent operations 277 300 * until 'ack' is set 278 301 */ 279 - if (!async_tx_test_ack(&desc->async_tx)) { 302 + if (!async_tx_test_ack(&desc->async_tx)) 280 303 /* move this slot to the completed_slots */ 281 - list_add_tail(&desc->completed_node, &mv_chan->completed_slots); 282 - return 0; 283 - } 304 + list_move_tail(&desc->node, &mv_chan->completed_slots); 305 + else 306 + list_move_tail(&desc->node, &mv_chan->free_slots); 284 307 285 - mv_chan_free_slots(mv_chan, desc); 286 308 return 0; 287 309 } 288 310 ··· 304 328 */ 305 329 306 330 list_for_each_entry_safe(iter, _iter, &mv_chan->chain, 307 - chain_node) { 331 + node) { 308 332 309 333 /* clean finished descriptors */ 310 334 hw_desc = iter->hw_desc; ··· 336 360 */ 337 361 iter = list_entry(mv_chan->chain.next, 338 362 struct mv_xor_desc_slot, 339 - chain_node); 363 + node); 340 364 mv_chan_start_new_chain(mv_chan, iter); 341 365 } else { 342 - if (!list_is_last(&iter->chain_node, &mv_chan->chain)) { 366 + if (!list_is_last(&iter->node, &mv_chan->chain)) { 343 367 /* 344 368 * descriptors are still waiting after 345 369 * current, trigger them 346 370 */ 347 - iter = list_entry(iter->chain_node.next, 371 + iter = list_entry(iter->node.next, 348 372 struct mv_xor_desc_slot, 349 - chain_node); 373 + node); 350 374 mv_chan_start_new_chain(mv_chan, iter); 351 375 } else { 352 376 /* ··· 374 398 static struct mv_xor_desc_slot * 375 399 mv_chan_alloc_slot(struct mv_xor_chan *mv_chan) 376 400 { 377 - struct mv_xor_desc_slot *iter, *_iter; 378 - int retry = 0; 401 + struct mv_xor_desc_slot *iter; 379 402 380 - /* start search from the last allocated descrtiptor 381 - * if a contiguous allocation can not be found start searching 382 - * from the beginning of the list 383 - */ 384 - retry: 385 - if (retry == 0) 386 - iter = mv_chan->last_used; 387 - else 388 - iter = list_entry(&mv_chan->all_slots, 389 - struct mv_xor_desc_slot, 390 - slot_node); 403 + spin_lock_bh(&mv_chan->lock); 391 404 392 - list_for_each_entry_safe_continue( 393 - iter, _iter, &mv_chan->all_slots, slot_node) { 405 + if (!list_empty(&mv_chan->free_slots)) { 406 + iter = list_first_entry(&mv_chan->free_slots, 407 + struct mv_xor_desc_slot, 408 + node); 394 409 395 - prefetch(_iter); 396 - prefetch(&_iter->async_tx); 397 - if (iter->slot_used) { 398 - /* give up after finding the first busy slot 399 - * on the second pass through the list 400 - */ 401 - if (retry) 402 - break; 403 - continue; 404 - } 410 + list_move_tail(&iter->node, &mv_chan->allocated_slots); 411 + 412 + spin_unlock_bh(&mv_chan->lock); 405 413 406 414 /* pre-ack descriptor */ 407 415 async_tx_ack(&iter->async_tx); 408 - 409 - iter->slot_used = 1; 410 - INIT_LIST_HEAD(&iter->chain_node); 411 416 iter->async_tx.cookie = -EBUSY; 412 - mv_chan->last_used = iter; 413 - mv_desc_clear_next_desc(iter); 414 417 415 418 return iter; 416 419 417 420 } 418 - if (!retry++) 419 - goto retry; 421 + 422 + spin_unlock_bh(&mv_chan->lock); 420 423 421 424 /* try to free some slots if the allocation fails */ 422 425 tasklet_schedule(&mv_chan->irq_tasklet); ··· 421 466 cookie = dma_cookie_assign(tx); 422 467 423 468 if (list_empty(&mv_chan->chain)) 424 - list_add_tail(&sw_desc->chain_node, &mv_chan->chain); 469 + list_move_tail(&sw_desc->node, &mv_chan->chain); 425 470 else { 426 471 new_hw_chain = 0; 427 472 428 473 old_chain_tail = list_entry(mv_chan->chain.prev, 429 474 struct mv_xor_desc_slot, 430 - chain_node); 431 - list_add_tail(&sw_desc->chain_node, &mv_chan->chain); 475 + node); 476 + list_move_tail(&sw_desc->node, &mv_chan->chain); 432 477 433 478 dev_dbg(mv_chan_to_devp(mv_chan), "Append to last desc %pa\n", 434 479 &old_chain_tail->async_tx.phys); ··· 481 526 482 527 dma_async_tx_descriptor_init(&slot->async_tx, chan); 483 528 slot->async_tx.tx_submit = mv_xor_tx_submit; 484 - INIT_LIST_HEAD(&slot->chain_node); 485 - INIT_LIST_HEAD(&slot->slot_node); 529 + INIT_LIST_HEAD(&slot->node); 486 530 dma_desc = mv_chan->dma_desc_pool; 487 531 slot->async_tx.phys = dma_desc + idx * MV_XOR_SLOT_SIZE; 488 532 slot->idx = idx++; 489 533 490 534 spin_lock_bh(&mv_chan->lock); 491 535 mv_chan->slots_allocated = idx; 492 - list_add_tail(&slot->slot_node, &mv_chan->all_slots); 536 + list_add_tail(&slot->node, &mv_chan->free_slots); 493 537 spin_unlock_bh(&mv_chan->lock); 494 538 } 495 539 496 - if (mv_chan->slots_allocated && !mv_chan->last_used) 497 - mv_chan->last_used = list_entry(mv_chan->all_slots.next, 498 - struct mv_xor_desc_slot, 499 - slot_node); 500 - 501 540 dev_dbg(mv_chan_to_devp(mv_chan), 502 - "allocated %d descriptor slots last_used: %p\n", 503 - mv_chan->slots_allocated, mv_chan->last_used); 541 + "allocated %d descriptor slots\n", 542 + mv_chan->slots_allocated); 504 543 505 544 return mv_chan->slots_allocated ? : -ENOMEM; 506 545 } ··· 515 566 "%s src_cnt: %d len: %u dest %pad flags: %ld\n", 516 567 __func__, src_cnt, len, &dest, flags); 517 568 518 - spin_lock_bh(&mv_chan->lock); 519 569 sw_desc = mv_chan_alloc_slot(mv_chan); 520 570 if (sw_desc) { 521 571 sw_desc->type = DMA_XOR; ··· 525 577 while (src_cnt--) 526 578 mv_desc_set_src_addr(sw_desc, src_cnt, src[src_cnt]); 527 579 } 528 - spin_unlock_bh(&mv_chan->lock); 580 + 529 581 dev_dbg(mv_chan_to_devp(mv_chan), 530 582 "%s sw_desc %p async_tx %p \n", 531 583 __func__, sw_desc, &sw_desc->async_tx); ··· 572 624 mv_chan_slot_cleanup(mv_chan); 573 625 574 626 list_for_each_entry_safe(iter, _iter, &mv_chan->chain, 575 - chain_node) { 627 + node) { 576 628 in_use_descs++; 577 - list_del(&iter->chain_node); 629 + list_move_tail(&iter->node, &mv_chan->free_slots); 578 630 } 579 631 list_for_each_entry_safe(iter, _iter, &mv_chan->completed_slots, 580 - completed_node) { 632 + node) { 581 633 in_use_descs++; 582 - list_del(&iter->completed_node); 634 + list_move_tail(&iter->node, &mv_chan->free_slots); 635 + } 636 + list_for_each_entry_safe(iter, _iter, &mv_chan->allocated_slots, 637 + node) { 638 + in_use_descs++; 639 + list_move_tail(&iter->node, &mv_chan->free_slots); 583 640 } 584 641 list_for_each_entry_safe_reverse( 585 - iter, _iter, &mv_chan->all_slots, slot_node) { 586 - list_del(&iter->slot_node); 642 + iter, _iter, &mv_chan->free_slots, node) { 643 + list_del(&iter->node); 587 644 kfree(iter); 588 645 mv_chan->slots_allocated--; 589 646 } 590 - mv_chan->last_used = NULL; 591 647 592 648 dev_dbg(mv_chan_to_devp(mv_chan), "%s slots_allocated %d\n", 593 649 __func__, mv_chan->slots_allocated); ··· 1049 1097 spin_lock_init(&mv_chan->lock); 1050 1098 INIT_LIST_HEAD(&mv_chan->chain); 1051 1099 INIT_LIST_HEAD(&mv_chan->completed_slots); 1052 - INIT_LIST_HEAD(&mv_chan->all_slots); 1100 + INIT_LIST_HEAD(&mv_chan->free_slots); 1101 + INIT_LIST_HEAD(&mv_chan->allocated_slots); 1053 1102 mv_chan->dmachan.device = dma_dev; 1054 1103 dma_cookie_init(&mv_chan->dmachan); 1055 1104
+6 -11
drivers/dma/mv_xor.h
··· 94 94 * @mmr_base: memory mapped register base 95 95 * @idx: the index of the xor channel 96 96 * @chain: device chain view of the descriptors 97 + * @free_slots: free slots usable by the channel 98 + * @allocated_slots: slots allocated by the driver 97 99 * @completed_slots: slots completed by HW but still need to be acked 98 100 * @device: parent device 99 101 * @common: common dmaengine channel object members 100 - * @last_used: place holder for allocation to continue from where it left off 101 - * @all_slots: complete domain of slots usable by the channel 102 102 * @slots_allocated: records the actual size of the descriptor slot pool 103 103 * @irq_tasklet: bottom half where mv_xor_slot_cleanup runs 104 104 * @op_in_desc: new mode of driver, each op is writen to descriptor. ··· 112 112 int irq; 113 113 enum dma_transaction_type current_type; 114 114 struct list_head chain; 115 + struct list_head free_slots; 116 + struct list_head allocated_slots; 115 117 struct list_head completed_slots; 116 118 dma_addr_t dma_desc_pool; 117 119 void *dma_desc_pool_virt; 118 120 size_t pool_size; 119 121 struct dma_device dmadev; 120 122 struct dma_chan dmachan; 121 - struct mv_xor_desc_slot *last_used; 122 - struct list_head all_slots; 123 123 int slots_allocated; 124 124 struct tasklet_struct irq_tasklet; 125 125 int op_in_desc; ··· 130 130 131 131 /** 132 132 * struct mv_xor_desc_slot - software descriptor 133 - * @slot_node: node on the mv_xor_chan.all_slots list 134 - * @chain_node: node on the mv_xor_chan.chain list 135 - * @completed_node: node on the mv_xor_chan.completed_slots list 133 + * @node: node on the mv_xor_chan lists 136 134 * @hw_desc: virtual address of the hardware descriptor chain 137 135 * @phys: hardware address of the hardware descriptor chain 138 136 * @slot_used: slot in use or not ··· 139 141 * @async_tx: support for the async_tx api 140 142 */ 141 143 struct mv_xor_desc_slot { 142 - struct list_head slot_node; 143 - struct list_head chain_node; 144 - struct list_head completed_node; 144 + struct list_head node; 145 145 enum dma_transaction_type type; 146 146 void *hw_desc; 147 - u16 slot_used; 148 147 u16 idx; 149 148 struct dma_async_tx_descriptor async_tx; 150 149 };