ahci: Fix data abort on multiple scsi resets.
authorRoger Quadros <rogerq@ti.com>
Tue, 1 Apr 2014 14:26:40 +0000 (17:26 +0300)
committerTom Rini <trini@ti.com>
Wed, 2 Apr 2014 19:44:40 +0000 (15:44 -0400)
Commit 2faf5fb82ed6 introduced a regression that causes a data
abort when running scsi init followed by scsi reset.

There are 2 problems with the original commit
1) ALLOC_CACHE_ALIGN_BUFFER() allocates memory on the stack but is
assigned to ataid[port] and used by other functions.
2) The function ata_scsiop_inquiry() tries to free memory which was
never allocated on the heap.

Fix these problems by using tmpid as a temporary cache aligned buffer.
Allocate memory separately for ataid[port] and re-use it if required.

Fixes: 2faf5fb82ed6 (ahci: Fix cache align error messages)
Reported-by: Eli Nidam <elini@marvell.com>
Signed-off-by: Roger Quadros <rogerq@ti.com>
drivers/block/ahci.c

index a409f63770473e5c3a83039d42e67ed5640a2363..c8f65739e9778a1578dc7aa17eb3448e9c749a46 100644 (file)
@@ -623,6 +623,7 @@ static int ata_scsiop_inquiry(ccb *pccb)
                95 - 4,
        };
        u8 fis[20];
+       u16 *idbuf;
        ALLOC_CACHE_ALIGN_BUFFER(u16, tmpid, ATA_ID_WORDS);
        u8 port;
 
@@ -649,17 +650,25 @@ static int ata_scsiop_inquiry(ccb *pccb)
                return -EIO;
        }
 
-       if (ataid[port])
-               free(ataid[port]);
-       ataid[port] = tmpid;
-       ata_swap_buf_le16(tmpid, ATA_ID_WORDS);
+       if (!ataid[port]) {
+               ataid[port] = malloc(ATA_ID_WORDS * 2);
+               if (!ataid[port]) {
+                       printf("%s: No memory for ataid[port]\n", __func__);
+                       return -ENOMEM;
+               }
+       }
+
+       idbuf = ataid[port];
+
+       memcpy(idbuf, tmpid, ATA_ID_WORDS * 2);
+       ata_swap_buf_le16(idbuf, ATA_ID_WORDS);
 
        memcpy(&pccb->pdata[8], "ATA     ", 8);
-       ata_id_strcpy((u16 *) &pccb->pdata[16], &tmpid[ATA_ID_PROD], 16);
-       ata_id_strcpy((u16 *) &pccb->pdata[32], &tmpid[ATA_ID_FW_REV], 4);
+       ata_id_strcpy((u16 *)&pccb->pdata[16], &idbuf[ATA_ID_PROD], 16);
+       ata_id_strcpy((u16 *)&pccb->pdata[32], &idbuf[ATA_ID_FW_REV], 4);
 
 #ifdef DEBUG
-       ata_dump_id(tmpid);
+       ata_dump_id(idbuf);
 #endif
        return 0;
 }