rsxx: Handling failed pci_map_page on PowerPC and double free.
authorPhilip J Kelleher <pjk1939@linux.vnet.ibm.com>
Wed, 4 Sep 2013 18:59:02 +0000 (13:59 -0500)
committerJens Axboe <axboe@kernel.dk>
Fri, 8 Nov 2013 16:10:28 +0000 (09:10 -0700)
The rsxx driver was not checking the correct value during a
pci_map_page failure. Fixing this also uncovered a
double free if the bio was returned before it was
broken up into indiviadual 4k dmas, that is also
fixed here.

Signed-off-by: Philip J Kelleher <pjk1939@linux.vnet.ibm.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
drivers/block/rsxx/core.c
drivers/block/rsxx/dma.c
drivers/block/rsxx/rsxx_priv.h

index 6e85e21..e740a65 100644 (file)
@@ -654,7 +654,8 @@ static void rsxx_eeh_failure(struct pci_dev *dev)
        for (i = 0; i < card->n_targets; i++) {
                spin_lock_bh(&card->ctrl[i].queue_lock);
                cnt = rsxx_cleanup_dma_queue(&card->ctrl[i],
-                                            &card->ctrl[i].queue);
+                                            &card->ctrl[i].queue,
+                                            COMPLETE_DMA);
                spin_unlock_bh(&card->ctrl[i].queue_lock);
 
                cnt += rsxx_dma_cancel(&card->ctrl[i]);
index bed32f1..71d1ca2 100644 (file)
@@ -221,6 +221,19 @@ static void dma_intr_coal_auto_tune(struct rsxx_cardinfo *card)
 }
 
 /*----------------- RSXX DMA Handling -------------------*/
+static void rsxx_free_dma(struct rsxx_dma_ctrl *ctrl, struct rsxx_dma *dma)
+{
+       if (!pci_dma_mapping_error(ctrl->card->dev, dma->dma_addr)) {
+               pci_unmap_page(ctrl->card->dev, dma->dma_addr,
+                              get_dma_size(dma),
+                              dma->cmd == HW_CMD_BLK_WRITE ?
+                                          PCI_DMA_TODEVICE :
+                                          PCI_DMA_FROMDEVICE);
+       }
+
+       kmem_cache_free(rsxx_dma_pool, dma);
+}
+
 static void rsxx_complete_dma(struct rsxx_dma_ctrl *ctrl,
                                  struct rsxx_dma *dma,
                                  unsigned int status)
@@ -232,21 +245,14 @@ static void rsxx_complete_dma(struct rsxx_dma_ctrl *ctrl,
        if (status & DMA_CANCELLED)
                ctrl->stats.dma_cancelled++;
 
-       if (dma->dma_addr)
-               pci_unmap_page(ctrl->card->dev, dma->dma_addr,
-                              get_dma_size(dma),
-                              dma->cmd == HW_CMD_BLK_WRITE ?
-                                          PCI_DMA_TODEVICE :
-                                          PCI_DMA_FROMDEVICE);
-
        if (dma->cb)
                dma->cb(ctrl->card, dma->cb_data, status ? 1 : 0);
 
-       kmem_cache_free(rsxx_dma_pool, dma);
+       rsxx_free_dma(ctrl, dma);
 }
 
 int rsxx_cleanup_dma_queue(struct rsxx_dma_ctrl *ctrl,
-                          struct list_head *q)
+                          struct list_head *q, unsigned int done)
 {
        struct rsxx_dma *dma;
        struct rsxx_dma *tmp;
@@ -254,7 +260,10 @@ int rsxx_cleanup_dma_queue(struct rsxx_dma_ctrl *ctrl,
 
        list_for_each_entry_safe(dma, tmp, q, list) {
                list_del(&dma->list);
-               rsxx_complete_dma(ctrl, dma, DMA_CANCELLED);
+               if (done & COMPLETE_DMA)
+                       rsxx_complete_dma(ctrl, dma, DMA_CANCELLED);
+               else
+                       rsxx_free_dma(ctrl, dma);
                cnt++;
        }
 
@@ -370,7 +379,7 @@ static void dma_engine_stalled(unsigned long data)
 
                /* Clean up the DMA queue */
                spin_lock(&ctrl->queue_lock);
-               cnt = rsxx_cleanup_dma_queue(ctrl, &ctrl->queue);
+               cnt = rsxx_cleanup_dma_queue(ctrl, &ctrl->queue, COMPLETE_DMA);
                spin_unlock(&ctrl->queue_lock);
 
                cnt += rsxx_dma_cancel(ctrl);
@@ -623,7 +632,7 @@ static int rsxx_queue_dma(struct rsxx_cardinfo *card,
        dma->dma_addr = pci_map_page(card->dev, page, pg_off, dma_len,
                                     dir ? PCI_DMA_TODEVICE :
                                     PCI_DMA_FROMDEVICE);
-       if (!dma->dma_addr) {
+       if (pci_dma_mapping_error(card->dev, dma->dma_addr)) {
                kmem_cache_free(rsxx_dma_pool, dma);
                return -ENOMEM;
        }
@@ -736,11 +745,9 @@ int rsxx_dma_queue_bio(struct rsxx_cardinfo *card,
        return 0;
 
 bvec_err:
-       for (i = 0; i < card->n_targets; i++) {
-               spin_lock_bh(&card->ctrl[i].queue_lock);
-               rsxx_cleanup_dma_queue(&card->ctrl[i], &dma_list[i]);
-               spin_unlock_bh(&card->ctrl[i].queue_lock);
-       }
+       for (i = 0; i < card->n_targets; i++)
+               rsxx_cleanup_dma_queue(&card->ctrl[i], &dma_list[i],
+                                       FREE_DMA);
 
        return st;
 }
@@ -990,7 +997,7 @@ void rsxx_dma_destroy(struct rsxx_cardinfo *card)
 
                /* Clean up the DMA queue */
                spin_lock_bh(&ctrl->queue_lock);
-               rsxx_cleanup_dma_queue(ctrl, &ctrl->queue);
+               rsxx_cleanup_dma_queue(ctrl, &ctrl->queue, COMPLETE_DMA);
                spin_unlock_bh(&ctrl->queue_lock);
 
                rsxx_dma_cancel(ctrl);
@@ -1045,7 +1052,7 @@ int rsxx_eeh_save_issued_dmas(struct rsxx_cardinfo *card)
                card->ctrl[i].e_cnt = 0;
 
                list_for_each_entry(dma, &card->ctrl[i].queue, list) {
-                       if (dma->dma_addr)
+                       if (!pci_dma_mapping_error(card->dev, dma->dma_addr))
                                pci_unmap_page(card->dev, dma->dma_addr,
                                               get_dma_size(dma),
                                               dma->cmd == HW_CMD_BLK_WRITE ?
@@ -1073,7 +1080,7 @@ int rsxx_eeh_remap_dmas(struct rsxx_cardinfo *card)
                                        dma->cmd == HW_CMD_BLK_WRITE ?
                                        PCI_DMA_TODEVICE :
                                        PCI_DMA_FROMDEVICE);
-                       if (!dma->dma_addr) {
+                       if (pci_dma_mapping_error(card->dev, dma->dma_addr)) {
                                spin_unlock_bh(&card->ctrl[i].queue_lock);
                                kmem_cache_free(rsxx_dma_pool, dma);
                                return -ENOMEM;
index 5ad5055..8277905 100644 (file)
@@ -345,6 +345,11 @@ enum rsxx_creg_stat {
        CREG_STAT_TAG_MASK      = 0x0000ff00,
 };
 
+enum rsxx_dma_finish {
+       FREE_DMA        = 0x0,
+       COMPLETE_DMA    = 0x1,
+};
+
 static inline unsigned int CREG_DATA(int N)
 {
        return CREG_DATA0 + (N << 2);
@@ -379,7 +384,9 @@ typedef void (*rsxx_dma_cb)(struct rsxx_cardinfo *card,
 int rsxx_dma_setup(struct rsxx_cardinfo *card);
 void rsxx_dma_destroy(struct rsxx_cardinfo *card);
 int rsxx_dma_init(void);
-int rsxx_cleanup_dma_queue(struct rsxx_dma_ctrl *ctrl, struct list_head *q);
+int rsxx_cleanup_dma_queue(struct rsxx_dma_ctrl *ctrl,
+                               struct list_head *q,
+                               unsigned int done);
 int rsxx_dma_cancel(struct rsxx_dma_ctrl *ctrl);
 void rsxx_dma_cleanup(void);
 void rsxx_dma_queue_reset(struct rsxx_cardinfo *card);