From f467043c10a7968361745139d9c0737a5331fcf9 Mon Sep 17 00:00:00 2001 From: Mihai Serban Date: Wed, 4 Nov 2015 17:35:46 +0200 Subject: 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 Reviewed-by: Beare, Bruce J Reviewed-by: Wood, Brian J --- drivers/spi/intel_mid_ssp_spi.c | 41 ++++++++++++++++++++++++++++------------- 1 file 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; } /** -- cgit v1.2.3