aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMihai Serban <mihai.serban@intel.com>2015-11-04 17:35:46 +0200
committerMohammed Habibulla <moch@google.com>2015-11-18 16:40:14 -0800
commitf467043c10a7968361745139d9c0737a5331fcf9 (patch)
tree48c6025c319574111c30cb5e0d12520af0b54ae6
parent57faed486dddeb7278d58d31a3883999de72d1f1 (diff)
downloadedison-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.c41
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;
}
/**