MLK-10048-3: ASoC: fsl_asrc: fix asrc crach when suspend/resume
authorShengjiu Wang <shengjiu.wang@freescale.com>
Mon, 8 Dec 2014 07:08:27 +0000 (15:08 +0800)
committerNitin Garg <nitin.garg@nxp.com>
Mon, 19 Mar 2018 19:48:03 +0000 (14:48 -0500)
merge 7e1a620a030d17f93fdd97d076f1cdd042e79337

The reason of crach is that some variables are not protected in
function mxc_asrc_suspend(), when suspend, there is possibility to
access one NULL pointer.
Refine the spin lock usage, add protecting for pair_hold.

Signed-off-by: Shengjiu Wang <shengjiu.wang@freescale.com>
(cherry picked from commit e90c73f8170bc929cff54b0478da0573e4e26c23)

sound/soc/fsl/fsl_asrc_m2m.c

index 2ea4009..97c9f6a 100644 (file)
@@ -369,9 +369,10 @@ int fsl_asrc_process_buffer(struct fsl_asrc_pair *pair,
                spin_unlock_irqrestore(&m2m->lock, lock_flags);
                return -EFAULT;
        }
-       fsl_asrc_read_last_FIFO(pair);
        spin_unlock_irqrestore(&m2m->lock, lock_flags);
 
+       fsl_asrc_read_last_FIFO(pair);
+
        /* Update final lengths after getting last FIFO */
        pbuf->input_buffer_length = m2m->dma_block[IN].length;
        pbuf->output_buffer_length = m2m->dma_block[OUT].length;
@@ -485,6 +486,7 @@ static long fsl_asrc_ioctl_req_pair(struct fsl_asrc_pair *pair,
        struct fsl_asrc_m2m *m2m = pair->private;
        struct device *dev = &asrc_priv->pdev->dev;
        struct asrc_req req;
+       unsigned long lock_flags;
        long ret;
 
        ret = copy_from_user(&req, user, sizeof(req));
@@ -499,7 +501,9 @@ static long fsl_asrc_ioctl_req_pair(struct fsl_asrc_pair *pair,
                return ret;
        }
 
+       spin_lock_irqsave(&m2m->lock, lock_flags);
        m2m->pair_hold = 1;
+       spin_unlock_irqrestore(&m2m->lock, lock_flags);
        pair->channels = req.chn_num;
 
        req.index = pair->index;
@@ -571,9 +575,6 @@ static long fsl_asrc_ioctl_config_pair(struct fsl_asrc_pair *pair,
                return  -EBUSY;
        }
 
-       init_completion(&m2m->complete[IN]);
-       init_completion(&m2m->complete[OUT]);
-
        ret = copy_to_user(user, &config, sizeof(config));
        if (ret) {
                pair_err("failed to send config to user space: %ld\n", ret);
@@ -824,6 +825,9 @@ static int fsl_asrc_open(struct inode *inode, struct file *file)
        pair->private = m2m;
        pair->asrc_priv = asrc_priv;
 
+       init_completion(&m2m->complete[IN]);
+       init_completion(&m2m->complete[OUT]);
+
        spin_lock_init(&m2m->lock);
 
        file->private_data = pair;
@@ -846,9 +850,11 @@ static int fsl_asrc_close(struct inode *inode, struct file *file)
                goto out;
 
        /* Make sure we have clear the pointer */
+       spin_lock_irqsave(&asrc_priv->lock, lock_flags);
        for (i = 0; i < ASRC_PAIR_MAX_NUM; i++)
                if (asrc_priv->pair[i] == pair)
                        asrc_priv->pair[i] = NULL;
+       spin_unlock_irqrestore(&asrc_priv->lock, lock_flags);
 
        if (m2m->asrc_active) {
                m2m->asrc_active = 0;
@@ -861,8 +867,8 @@ static int fsl_asrc_close(struct inode *inode, struct file *file)
                fsl_asrc_output_dma_callback((void *)pair);
        }
 
+       spin_lock_irqsave(&m2m->lock, lock_flags);
        if (m2m->pair_hold) {
-               spin_lock_irqsave(&m2m->lock, lock_flags);
                m2m->pair_hold = 0;
                spin_unlock_irqrestore(&m2m->lock, lock_flags);
 
@@ -875,10 +881,13 @@ static int fsl_asrc_close(struct inode *inode, struct file *file)
                kfree(m2m->dma_block[OUT].dma_vaddr);
 
                fsl_asrc_release_pair(pair);
-       }
+       } else
+               spin_unlock_irqrestore(&m2m->lock, lock_flags);
 
+       spin_lock_irqsave(&asrc_priv->lock, lock_flags);
        kfree(m2m);
        kfree(pair);
+       spin_unlock_irqrestore(&asrc_priv->lock, lock_flags);
        file->private_data = NULL;
 
 out:
@@ -920,22 +929,29 @@ static void fsl_asrc_m2m_suspend(struct fsl_asrc *asrc_priv)
 {
        struct fsl_asrc_pair *pair;
        struct fsl_asrc_m2m *m2m;
+       unsigned long lock_flags;
        int i;
 
        for (i = 0; i < ASRC_PAIR_MAX_NUM; i++) {
+               spin_lock_irqsave(&asrc_priv->lock, lock_flags);
                pair = asrc_priv->pair[i];
-               if (!pair || !pair->private)
+               if (!pair || !pair->private) {
+                       spin_unlock_irqrestore(&asrc_priv->lock, lock_flags);
                        continue;
-
+               }
                m2m = pair->private;
 
                if (!completion_done(&m2m->complete[IN])) {
-                       dmaengine_terminate_all(pair->dma_chan[IN]);
+                       if (pair->dma_chan[IN])
+                               dmaengine_terminate_all(pair->dma_chan[IN]);
                        fsl_asrc_input_dma_callback((void *)pair);
                }
                if (!completion_done(&m2m->complete[OUT])) {
-                       dmaengine_terminate_all(pair->dma_chan[OUT]);
+                       if (pair->dma_chan[OUT])
+                               dmaengine_terminate_all(pair->dma_chan[OUT]);
                        fsl_asrc_output_dma_callback((void *)pair);
                }
+
+               spin_unlock_irqrestore(&asrc_priv->lock, lock_flags);
        }
 }