diff options
author | Mihai Serban <mihai.serban@intel.com> | 2015-11-04 17:35:46 +0200 |
---|---|---|
committer | Mohammed Habibulla <moch@google.com> | 2015-11-18 16:40:14 -0800 |
commit | f467043c10a7968361745139d9c0737a5331fcf9 (patch) | |
tree | 48c6025c319574111c30cb5e0d12520af0b54ae6 | |
parent | 57faed486dddeb7278d58d31a3883999de72d1f1 (diff) | |
download | edison-v3.10-f467043c10a7968361745139d9c0737a5331fcf9.tar.gz |
intel: spi: fix memory allocation inside atomic context
The setup function allocates memory for chip_data structure while holding
a spin lock. Instead of using atomic memory allocation we can temporary
exit the atomic context, allocate the required memory, re-acquire the
spin lock and check if there was a race before continuing or not.
BUG=25746315
Change-Id: I89495774b3c3a5afdc3dcfa84ce9c43d0a4ea04a
Tracked-On: https://jira01.devtools.intel.com/browse/BP-25
Signed-off-by: Mihai Serban <mihai.serban@intel.com>
Reviewed-by: Beare, Bruce J <bruce.j.beare@intel.com>
Reviewed-by: Wood, Brian J <brian.j.wood@intel.com>
-rw-r--r-- | drivers/spi/intel_mid_ssp_spi.c | 41 |
1 files changed, 28 insertions, 13 deletions
diff --git a/drivers/spi/intel_mid_ssp_spi.c b/drivers/spi/intel_mid_ssp_spi.c index 173a154f9f0..6e430c0317a 100644 --- a/drivers/spi/intel_mid_ssp_spi.c +++ b/drivers/spi/intel_mid_ssp_spi.c @@ -1240,7 +1240,7 @@ static void pump_messages(struct work_struct *work) static int setup(struct spi_device *spi) { struct intel_mid_ssp_spi_chip *chip_info = NULL; - struct chip_data *chip; + struct chip_data *chip, *alloc_chip = NULL; struct ssp_drv_context *sspc = spi_master_get_devdata(spi->master); u32 tx_fifo_threshold; @@ -1248,6 +1248,7 @@ static int setup(struct spi_device *spi) u32 clk_div; static u32 one_time_setup = 1; unsigned long flags; + int ret = 0; spin_lock_irqsave(&sspc->lock, flags); if (!spi->bits_per_word) @@ -1255,20 +1256,29 @@ static int setup(struct spi_device *spi) if ((spi->bits_per_word < MIN_BITS_PER_WORD || spi->bits_per_word > MAX_BITS_PER_WORD)) { - spin_unlock_irqrestore(&sspc->lock, flags); - return -EINVAL; + ret = -EINVAL; + goto exit_setup; } chip = spi_get_ctldata(spi); if (!chip) { - chip = kzalloc(sizeof(struct chip_data), GFP_KERNEL); + spin_unlock_irqrestore(&sspc->lock, flags); + alloc_chip = chip = kzalloc(sizeof(struct chip_data), GFP_KERNEL); + spin_lock_irqsave(&sspc->lock, flags); if (!chip) { dev_err(&spi->dev, "failed setup: can't allocate chip data\n"); - spin_unlock_irqrestore(&sspc->lock, flags); - return -ENOMEM; + ret = -ENOMEM; + goto exit_setup; + } + + if (spi_get_ctldata(spi)) { + dev_err(&spi->dev, "failed setup: already executed\n"); + ret = -EAGAIN; + goto exit_setup; } } + chip->cr0 = SSCR0_Motorola | SSCR0_DataSize(spi->bits_per_word > 16 ? spi->bits_per_word - 16 : spi->bits_per_word) | SSCR0_SSE @@ -1287,15 +1297,15 @@ static int setup(struct spi_device *spi) if (sspc->cur_msg) { dev_err(&spi->dev, "message pending... Failing\n"); /* A message is currently in transfer. Do not toggle CS */ - spin_unlock_irqrestore(&sspc->lock, flags); - return -EAGAIN; + ret = -EAGAIN; + goto exit_setup; } if (!chip_info->cs_control) { /* unable to control cs by hand */ dev_err(&spi->dev, "This CS does not support SPI_CS_HIGH flag\n"); - spin_unlock_irqrestore(&sspc->lock, flags); - return -EINVAL; + ret = -EINVAL; + goto exit_setup; } sspc->cs_assert = spi->mode & SPI_CS_HIGH; chip_info->cs_control(!sspc->cs_assert); @@ -1372,8 +1382,8 @@ static int setup(struct spi_device *spi) chip->n_bytes = 4; } else { dev_err(&spi->dev, "invalid wordsize\n"); - spin_unlock_irqrestore(&sspc->lock, flags); - return -EINVAL; + ret = -EINVAL; + goto exit_setup; } if ((sspc->quirks & QUIRKS_SPI_SLAVE_CLOCK_MODE) == 0) { @@ -1387,6 +1397,7 @@ static int setup(struct spi_device *spi) chip->bits_per_word = spi->bits_per_word; chip->chip_select = spi->chip_select; + alloc_chip = NULL; spi_set_ctldata(spi, chip); /* setup of sspc members that will not change across transfers */ @@ -1406,8 +1417,12 @@ static int setup(struct spi_device *spi) } sspc->clear_sr = SSSR_TUR | SSSR_ROR | SSSR_TINT; +exit_setup: + if (alloc_chip) + kfree(alloc_chip); + spin_unlock_irqrestore(&sspc->lock, flags); - return 0; + return ret; } /** |