From a537fc2dce58424510fc514eb499c544ae1ac96e Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Thu, 26 Aug 2010 10:18:40 +0530 Subject: [arm/tegra] dma: Dma allocation should be thread safe The dma can be allocated from multiple client in run time and so it should be thread/smp safe. Returning proper error pointer in case of there is no dma to allocate. bug 723220 Change-Id: Ifb333d4b14e32be561e34a0d7668a2d631ac80c6 (cherry picked from commit db2d10f715fcdd6fdaf5fc7ea8e27a505f8332da) Reviewed-on: http://git-master/r/5769 Reviewed-by: Laxman Dewangan Tested-by: Laxman Dewangan Reviewed-by: Bharat Nihalani --- arch/arm/mach-tegra/dma.c | 17 +++++++++++++++-- arch/arm/mach-tegra/nvrm/io/ap15/rm_spi_slink.c | 6 +++--- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/arch/arm/mach-tegra/dma.c b/arch/arm/mach-tegra/dma.c index 7eb0bb758b67..ad9f3a05a68b 100644 --- a/arch/arm/mach-tegra/dma.c +++ b/arch/arm/mach-tegra/dma.c @@ -132,6 +132,8 @@ struct tegra_dma_channel { #define NV_DMA_MAX_CHANNELS 32 +static DEFINE_SPINLOCK(global_dma_lock); + static DECLARE_BITMAP(channel_usage, NV_DMA_MAX_CHANNELS); static struct tegra_dma_channel dma_channels[NV_DMA_MAX_CHANNELS]; @@ -471,6 +473,9 @@ struct tegra_dma_channel *tegra_dma_allocate_channel(int mode) { int channel; struct tegra_dma_channel *ch; + unsigned long irq_flags; + + spin_lock_irqsave(&global_dma_lock, irq_flags); /* first channel is the shared channel */ if (mode & TEGRA_DMA_SHARED) { @@ -478,10 +483,14 @@ struct tegra_dma_channel *tegra_dma_allocate_channel(int mode) } else { channel = find_first_zero_bit(channel_usage, ARRAY_SIZE(dma_channels)); - if (channel >= ARRAY_SIZE(dma_channels)) - return ERR_PTR(ENODEV); + if (channel >= ARRAY_SIZE(dma_channels)) { + spin_unlock_irqrestore(&global_dma_lock, irq_flags); + return ERR_PTR(-ENODEV); + } } __set_bit(channel, channel_usage); + spin_unlock_irqrestore(&global_dma_lock, irq_flags); + ch = &dma_channels[channel]; ch->mode = mode; return ch; @@ -490,10 +499,14 @@ EXPORT_SYMBOL(tegra_dma_allocate_channel); void tegra_dma_free_channel(struct tegra_dma_channel *ch) { + unsigned long irq_flags; if (ch->mode & TEGRA_DMA_SHARED) return; tegra_dma_cancel(ch); + spin_lock_irqsave(&global_dma_lock, irq_flags); __clear_bit(ch->id, channel_usage); + spin_unlock_irqrestore(&global_dma_lock, irq_flags); + } EXPORT_SYMBOL(tegra_dma_free_channel); diff --git a/arch/arm/mach-tegra/nvrm/io/ap15/rm_spi_slink.c b/arch/arm/mach-tegra/nvrm/io/ap15/rm_spi_slink.c index cf7fd9493820..26ee1e1a6457 100644 --- a/arch/arm/mach-tegra/nvrm/io/ap15/rm_spi_slink.c +++ b/arch/arm/mach-tegra/nvrm/io/ap15/rm_spi_slink.c @@ -57,7 +57,7 @@ #include "linux/module.h" #include "mach/dma.h" - +#include "linux/err.h" // Combined maximum spi/slink controllers #define MAX_SPI_SLINK_INSTANCE (MAX_SLINK_CONTROLLERS + MAX_SPI_CONTROLLERS) @@ -513,10 +513,10 @@ static NvError AllocateDmas(NvRmSpiHandle hRmSpiSlink) hRmSpiSlink->hTxDma = NULL; hRmSpiSlink->hRxDma = tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT); - if (hRmSpiSlink->hRxDma) + if (!IS_ERR_OR_NULL(hRmSpiSlink->hRxDma)) { hRmSpiSlink->hTxDma = tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT); - if (!hRmSpiSlink->hTxDma) + if (IS_ERR_OR_NULL(hRmSpiSlink->hTxDma)) { tegra_dma_free_channel(hRmSpiSlink->hRxDma); hRmSpiSlink->hRxDma = NULL; -- cgit v1.2.3