caif-shm: Bugfixes for caif_shmcore.c
authorsjur.brandeland@stericsson.com <sjur.brandeland@stericsson.com>
Tue, 6 Dec 2011 12:15:43 +0000 (12:15 +0000)
committerDavid S. Miller <davem@davemloft.net>
Tue, 6 Dec 2011 22:21:47 +0000 (17:21 -0500)
Various bugfixes for caif_shmcore.c:
- fix deadlocks due to improper usage of spin-lock
- add missing spin-lock init
- don't call dev_kfree_skb() with irqs disabled,
  use dev_kfree_skb_irq() instead.
- fix potential skb null pointer de-reference.

Squashed original patches from:
Rabin Vincent <rabin.vincent@stericsson.com>
Durga Prasada Rao BATHINA <durgaprasadarao.b@stericcson.com>
Arun Murthy <arun.murthy@stericsson.com>
Bibek Basu <bibek.basu@stericsson.com>

Signed-off-by: Sjur Brændeland <sjur.brandeland@stericsson.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/caif/caif_shmcore.c

index d4b26fb..fd59e37 100644 (file)
@@ -238,11 +238,11 @@ int caif_shmdrv_rx_cb(u32 mbx_msg, void *priv)
                if ((avail_emptybuff > HIGH_WATERMARK) &&
                                        (!pshm_drv->tx_empty_available)) {
                        pshm_drv->tx_empty_available = 1;
+                       spin_unlock_irqrestore(&pshm_drv->lock, flags);
                        pshm_drv->cfdev.flowctrl
                                        (pshm_drv->pshm_dev->pshm_netdev,
                                                                CAIF_FLOW_ON);
 
-                       spin_unlock_irqrestore(&pshm_drv->lock, flags);
 
                        /* Schedule the work queue. if required */
                        if (!work_pending(&pshm_drv->shm_tx_work))
@@ -285,6 +285,7 @@ static void shm_rx_work_func(struct work_struct *rx_work)
                        list_entry(pshm_drv->rx_full_list.next, struct buf_list,
                                        list);
                list_del_init(&pbuf->list);
+               spin_unlock_irqrestore(&pshm_drv->lock, flags);
 
                /* Retrieve pointer to start of the packet descriptor area. */
                pck_desc = (struct shm_pck_desc *) pbuf->desc_vptr;
@@ -360,6 +361,7 @@ static void shm_rx_work_func(struct work_struct *rx_work)
                        pck_desc++;
                }
 
+               spin_lock_irqsave(&pshm_drv->lock, flags);
                list_add_tail(&pbuf->list, &pshm_drv->rx_pend_list);
 
                spin_unlock_irqrestore(&pshm_drv->lock, flags);
@@ -412,7 +414,6 @@ static void shm_tx_work_func(struct work_struct *tx_work)
 
                if (skb == NULL)
                        goto send_msg;
-
                /* Check the available no. of buffers in the empty list */
                list_for_each(pos, &pshm_drv->tx_empty_list)
                        avail_emptybuff++;
@@ -421,9 +422,11 @@ static void shm_tx_work_func(struct work_struct *tx_work)
                                        pshm_drv->tx_empty_available) {
                        /* Update blocking condition. */
                        pshm_drv->tx_empty_available = 0;
+                       spin_unlock_irqrestore(&pshm_drv->lock, flags);
                        pshm_drv->cfdev.flowctrl
                                        (pshm_drv->pshm_dev->pshm_netdev,
                                        CAIF_FLOW_OFF);
+                       spin_lock_irqsave(&pshm_drv->lock, flags);
                }
                /*
                 * We simply return back to the caller if we do not have space
@@ -469,6 +472,8 @@ static void shm_tx_work_func(struct work_struct *tx_work)
                        }
 
                        skb = skb_dequeue(&pshm_drv->sk_qhead);
+                       if (skb == NULL)
+                               break;
                        /* Copy in CAIF frame. */
                        skb_copy_bits(skb, 0, pbuf->desc_vptr +
                                        pbuf->frm_ofs + SHM_HDR_LEN +
@@ -477,7 +482,7 @@ static void shm_tx_work_func(struct work_struct *tx_work)
                        pshm_drv->pshm_dev->pshm_netdev->stats.tx_packets++;
                        pshm_drv->pshm_dev->pshm_netdev->stats.tx_bytes +=
                                                                        frmlen;
-                       dev_kfree_skb(skb);
+                       dev_kfree_skb_irq(skb);
 
                        /* Fill in the shared memory packet descriptor area. */
                        pck_desc = (struct shm_pck_desc *) (pbuf->desc_vptr);
@@ -512,16 +517,11 @@ send_msg:
 static int shm_netdev_tx(struct sk_buff *skb, struct net_device *shm_netdev)
 {
        struct shmdrv_layer *pshm_drv;
-       unsigned long flags = 0;
 
        pshm_drv = netdev_priv(shm_netdev);
 
-       spin_lock_irqsave(&pshm_drv->lock, flags);
-
        skb_queue_tail(&pshm_drv->sk_qhead, skb);
 
-       spin_unlock_irqrestore(&pshm_drv->lock, flags);
-
        /* Schedule Tx work queue. for deferred processing of skbs*/
        if (!work_pending(&pshm_drv->shm_tx_work))
                queue_work(pshm_drv->pshm_tx_workqueue, &pshm_drv->shm_tx_work);
@@ -606,6 +606,7 @@ int caif_shmcore_probe(struct shmdev_layer *pshm_dev)
                pshm_drv->shm_rx_addr = pshm_dev->shm_base_addr +
                                                (NR_TX_BUF * TX_BUF_SZ);
 
+       spin_lock_init(&pshm_drv->lock);
        INIT_LIST_HEAD(&pshm_drv->tx_empty_list);
        INIT_LIST_HEAD(&pshm_drv->tx_pend_list);
        INIT_LIST_HEAD(&pshm_drv->tx_full_list);
@@ -640,7 +641,7 @@ int caif_shmcore_probe(struct shmdev_layer *pshm_dev)
                tx_buf->frm_ofs = SHM_CAIF_FRM_OFS;
 
                if (pshm_dev->shm_loopback)
-                       tx_buf->desc_vptr = (char *)tx_buf->phy_addr;
+                       tx_buf->desc_vptr = (unsigned char *)tx_buf->phy_addr;
                else
                        tx_buf->desc_vptr =
                                        ioremap(tx_buf->phy_addr, TX_BUF_SZ);
@@ -664,7 +665,7 @@ int caif_shmcore_probe(struct shmdev_layer *pshm_dev)
                rx_buf->len = RX_BUF_SZ;
 
                if (pshm_dev->shm_loopback)
-                       rx_buf->desc_vptr = (char *)rx_buf->phy_addr;
+                       rx_buf->desc_vptr = (unsigned char *)rx_buf->phy_addr;
                else
                        rx_buf->desc_vptr =
                                        ioremap(rx_buf->phy_addr, RX_BUF_SZ);