aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Williams <dan.j.williams@intel.com>2009-03-25 12:13:23 -0400
committerDan Williams <dan.j.williams@intel.com>2009-03-25 12:13:23 -0400
commit257b17ca030387cb17314cd1851507bdd1b4ddd5 (patch)
tree74f88050ecfb70e6370399bc8b34843b22472f85
parent041b62374c7fedc11a8a1eeda2868612d3d1436c (diff)
dmaengine: fail device registration if channel registration fails
Atsushi points out: "If alloc_percpu or kzalloc failed, chan_id does not match with its position in device->channels list. And above "continue" looks buggy anyway. Keeping incomplete channels in device->channels list looks very dangerous..." Also, fix up leakage of idr_ref in the idr_pre_get() and channel init fail cases. Reported-by: Atsushi Nemoto <anemo@mba.ocn.ne.jp> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
-rw-r--r--drivers/dma/dmaengine.c51
1 files changed, 39 insertions, 12 deletions
diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c
index 280a9d263eb3..49243d14b894 100644
--- a/drivers/dma/dmaengine.c
+++ b/drivers/dma/dmaengine.c
@@ -602,6 +602,24 @@ void dmaengine_put(void)
602} 602}
603EXPORT_SYMBOL(dmaengine_put); 603EXPORT_SYMBOL(dmaengine_put);
604 604
605static int get_dma_id(struct dma_device *device)
606{
607 int rc;
608
609 idr_retry:
610 if (!idr_pre_get(&dma_idr, GFP_KERNEL))
611 return -ENOMEM;
612 mutex_lock(&dma_list_mutex);
613 rc = idr_get_new(&dma_idr, NULL, &device->dev_id);
614 mutex_unlock(&dma_list_mutex);
615 if (rc == -EAGAIN)
616 goto idr_retry;
617 else if (rc != 0)
618 return rc;
619
620 return 0;
621}
622
605/** 623/**
606 * dma_async_device_register - registers DMA devices found 624 * dma_async_device_register - registers DMA devices found
607 * @device: &dma_device 625 * @device: &dma_device
@@ -640,27 +658,25 @@ int dma_async_device_register(struct dma_device *device)
640 idr_ref = kmalloc(sizeof(*idr_ref), GFP_KERNEL); 658 idr_ref = kmalloc(sizeof(*idr_ref), GFP_KERNEL);
641 if (!idr_ref) 659 if (!idr_ref)
642 return -ENOMEM; 660 return -ENOMEM;
643 atomic_set(idr_ref, 0); 661 rc = get_dma_id(device);
644 idr_retry: 662 if (rc != 0) {
645 if (!idr_pre_get(&dma_idr, GFP_KERNEL)) 663 kfree(idr_ref);
646 return -ENOMEM;
647 mutex_lock(&dma_list_mutex);
648 rc = idr_get_new(&dma_idr, NULL, &device->dev_id);
649 mutex_unlock(&dma_list_mutex);
650 if (rc == -EAGAIN)
651 goto idr_retry;
652 else if (rc != 0)
653 return rc; 664 return rc;
665 }
666
667 atomic_set(idr_ref, 0);
654 668
655 /* represent channels in sysfs. Probably want devs too */ 669 /* represent channels in sysfs. Probably want devs too */
656 list_for_each_entry(chan, &device->channels, device_node) { 670 list_for_each_entry(chan, &device->channels, device_node) {
671 rc = -ENOMEM;
657 chan->local = alloc_percpu(typeof(*chan->local)); 672 chan->local = alloc_percpu(typeof(*chan->local));
658 if (chan->local == NULL) 673 if (chan->local == NULL)
659 continue; 674 goto err_out;
660 chan->dev = kzalloc(sizeof(*chan->dev), GFP_KERNEL); 675 chan->dev = kzalloc(sizeof(*chan->dev), GFP_KERNEL);
661 if (chan->dev == NULL) { 676 if (chan->dev == NULL) {
662 free_percpu(chan->local); 677 free_percpu(chan->local);
663 continue; 678 chan->local = NULL;
679 goto err_out;
664 } 680 }
665 681
666 chan->chan_id = chancnt++; 682 chan->chan_id = chancnt++;
@@ -677,6 +693,8 @@ int dma_async_device_register(struct dma_device *device)
677 if (rc) { 693 if (rc) {
678 free_percpu(chan->local); 694 free_percpu(chan->local);
679 chan->local = NULL; 695 chan->local = NULL;
696 kfree(chan->dev);
697 atomic_dec(idr_ref);
680 goto err_out; 698 goto err_out;
681 } 699 }
682 chan->client_count = 0; 700 chan->client_count = 0;
@@ -707,6 +725,15 @@ int dma_async_device_register(struct dma_device *device)
707 return 0; 725 return 0;
708 726
709err_out: 727err_out:
728 /* if we never registered a channel just release the idr */
729 if (atomic_read(idr_ref) == 0) {
730 mutex_lock(&dma_list_mutex);
731 idr_remove(&dma_idr, device->dev_id);
732 mutex_unlock(&dma_list_mutex);
733 kfree(idr_ref);
734 return rc;
735 }
736
710 list_for_each_entry(chan, &device->channels, device_node) { 737 list_for_each_entry(chan, &device->channels, device_node) {
711 if (chan->local == NULL) 738 if (chan->local == NULL)
712 continue; 739 continue;