/******************************************************************************* NAME $RCSfile: sataTest.c,v $ SUMMARY SATA device Test VERSION $Revision: 1.9 $ UPDATE DATE $Date: 2009/12/07 06:16:13 $ PROGRAMMER $Author: lecter $ Copyright 2009 LSI Corporation. All Rights Reserved. DESCRIPTION: The functions used to diagnose SATA device REFERENCE: *******************************************************************************/ #define __SRCsataTest /*** INCLUDES ***/ #include #include #include #include #include #include #include #include #include #include "commSCSI.h" #include "commPattern.h" #include "commRpt.h" #include "commMEC.h" #include "sataStruct.h" /*** MACRO DEFINITIONS ***/ #define M_SATA_REPORT_PROGRESS(str, cur, rpt, incr) do { \ cur+=incr; \ if ((cur > 0) && (cur > rpt)) { \ commPostProgress(str, cur, SATA_TEST_TOTAL_PROGRESS_UINT); \ rpt = cur; \ } else if ((incr == 0) && (cur != SATA_TEST_TOTAL_PROGRESS_UINT)){ \ commPostProgress(str, SATA_TEST_TOTAL_PROGRESS_UINT, SATA_TEST_TOTAL_PROGRESS_UINT); \ } \ } while (0) /*** EXTERNAL REFERENCES ***/ extern unsigned long gDiagAvailAddr; extern UINT8 sataGetDeviceList(struct pci_dev* pDev, UINT8 idxSATAPort, struct scsi_device** pSCSIDev); extern void sataDiagDevice(struct pci_dev* pDev, UINT8 idxSATAPort, struct scsi_device** pSCSIDev, UINT8* numSATADev); /*** FORWARE REFERENCES ***/ LOCAL UINT32 sataGetTotalSector(struct scsi_device *pSDev, INT8 itemCode); LOCAL VOID sataDiagCapacity(struct scsi_device *pSDev, UINT8 idxPort); LOCAL VOID sataDiagTransfer(struct scsi_device *pSDev, UINT8 idxPort); LOCAL VOID sataDiagRandWriteVerify(struct scsi_device *pSDev, UINT8 idxPort) ; LOCAL VOID sataStopRWVTest(SATA_RWVT* pRWVTest); LOCAL VOID sataDiagPerformance(struct scsi_device *pSDev, UINT8 idxPort); LOCAL UINT32 sataTransferVerify(VOID* pSrcBuf, VOID* pDstBuf, UINT32 length); LOCAL UINT16 sataGetRandom(VOID); /*** GLOBAL DATA ***/ UINT8 gExpSATADeviceNum = SATA_TEST_DEFAULT_SSD_DEVICE; UINT8 gMaxSATADeviceNum = SATA_TEST_MAX_SSD_DEVICE; /*** PROCEDURES ***/ /******************************************************************************* * PROCEDURE * * NAME: sataIntfaceTest * SUMMARY: SATA Infterface Test * SCOPE: public * * DESCRIPTION: This function perform the diagnostics on the SATA inferface * * RETURNS: * * NOTES: * */ PUBLIC VOID sataIntfaceTest(UINT8 idxPort) { struct pci_dev* pDev = NULL; struct scsi_device* listSATATest[gMaxSATADeviceNum]; UINT8 idxDev; UINT8 numSATADev; /*Using the vender id and device id of ahci controller to get pci device*/ pDev = pci_get_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PCH, pDev); if(pDev == NULL) { commReportError(MEC_COMP_CODE_TEMP, SATA_DIAG_ITEM_CODE_P0_INTERFACE + idxPort, SATA_DIAG_ERROR_CODE_HOST_DETECT_FAILED, SATA_DIAG_ERROR_MSG_HOST_DETECT_FAILED, 0, 0, 0, DG_FORMAT_NONE); return; } /* Perform Interface diagnostics on this ahci controller to check if its port setting and device setting is correct */ numSATADev = 0; sataDiagDevice(pDev, idxPort, listSATATest, &numSATADev); if(numSATADev < SATA_TEST_DEFAULT_SSD_DEVICE) { commReportError(MEC_COMP_CODE_TEMP, SATA_DIAG_ITEM_CODE_P0_INTERFACE + idxPort, SATA_DIAG_ERROR_CODE_DEVICE_DETECT_FAILED, SATA_DIAG_ERROR_MSG_DEVICE_DETECT_FAILED, SATA_TEST_DEFAULT_SSD_DEVICE, numSATADev, 0, DG_PCH_MSG_FORMAT); return; } if(listSATATest[idxDev] == NULL){ commReportError(MEC_COMP_CODE_TEMP, SATA_DIAG_ITEM_CODE_P0_INTERFACE + idxPort, SATA_DIAG_ERROR_CODE_HOST_DETECT_FAILED, SATA_DIAG_ERROR_MSG_HOST_DETECT_FAILED, 0, 0, 0, DG_FORMAT_NONE); return; } for(idxDev = 0; idxDev < numSATADev; idxDev++) { ASSERT(listSATATest[idxDev] != NULL); sataDiagCapacity(listSATATest[idxDev], idxPort); } } /******************************************************************************* * PROCEDURE * * NAME: sataTransferTest * SUMMARY: USB Data Transfer Test * SCOPE: public * * DESCRIPTION: This function peform SATA data transfer test * * RETURNS: * * NOTES: * */ PUBLIC VOID sataTransferTest(UINT8 idxPort) { struct pci_dev* pDev = NULL; struct scsi_device* listSATATest[gMaxSATADeviceNum]; UINT8 idxDev; UINT8 numSATADev; /* Using the vender id and device id of ahci controller to get pci device */ pDev = pci_get_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PCH, pDev); if(pDev == NULL) { commReportError(MEC_COMP_CODE_TEMP, SATA_DIAG_ITEM_CODE_P0_TRANSFER + idxPort, SATA_DIAG_ERROR_CODE_HOST_DETECT_FAILED, SATA_DIAG_ERROR_MSG_HOST_DETECT_FAILED, 0, 0, 0, DG_FORMAT_NONE); return; } /* Perform Transfer diagnostics on this ahci controller to check if it could transfer data correctly */ numSATADev = 0; numSATADev = sataGetDeviceList(pDev, idxPort, listSATATest); if(listSATATest[idxDev] == NULL){ commReportError(MEC_COMP_CODE_TEMP, SATA_DIAG_ITEM_CODE_P0_INTERFACE + idxPort, SATA_DIAG_ERROR_CODE_HOST_DETECT_FAILED, SATA_DIAG_ERROR_MSG_HOST_DETECT_FAILED, 0, 0, 0, DG_FORMAT_NONE); return; } for(idxDev = 0; idxDev < numSATADev; idxDev++) { ASSERT(listSATATest[idxDev] != NULL); sataDiagTransfer(listSATATest[idxDev], idxPort); } if(numSATADev == 0) { commReportError(MEC_COMP_CODE_TEMP, SATA_DIAG_ITEM_CODE_P0_TRANSFER + idxPort, SATA_DIAG_ERROR_CODE_DEVICE_DETECT_FAILED, SATA_DIAG_ERROR_MSG_DEVICE_DETECT_FAILED, SATA_TEST_DEFAULT_SSD_DEVICE, numSATADev, 0, DG_PCH_MSG_FORMAT); } } /******************************************************************************* * PROCEDURE * * NAME: sataRandWriteVerifyTest * SUMMARY: SATA random write verify Test * SCOPE: public * * DESCRIPTION: This function peform SATA random write verify Test * * RETURNS: * * NOTES: * */ PUBLIC VOID sataRandWriteVerifyTest(UINT8 idxPort) { struct pci_dev* pDev = NULL; struct scsi_device* listSATATest[gMaxSATADeviceNum]; UINT8 idxDev; UINT8 numSATADev; /* Using the vender id and device id of ahci controller to get pci device */ pDev = pci_get_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PCH, pDev); if(pDev == NULL) { commReportError(MEC_COMP_CODE_TEMP, SATA_DIAG_ITEM_CODE_P0_RAND_WRITE_VERIFY + idxPort, SATA_DIAG_ERROR_CODE_HOST_DETECT_FAILED, SATA_DIAG_ERROR_MSG_HOST_DETECT_FAILED, 0, 0, 0, DG_FORMAT_NONE); return; } /* Perform Random Write Verify diagnostics on this ahci controller to check if it could handle 32 concurrency commands correctly*/ numSATADev = 0; idxPort = 0; numSATADev = sataGetDeviceList(pDev, idxPort, listSATATest); if(listSATATest[idxDev] == NULL){ commReportError(MEC_COMP_CODE_TEMP, SATA_DIAG_ITEM_CODE_P0_INTERFACE + idxPort, SATA_DIAG_ERROR_CODE_HOST_DETECT_FAILED, SATA_DIAG_ERROR_MSG_HOST_DETECT_FAILED, 0, 0, 0, DG_FORMAT_NONE); return; } for(idxDev = 0; idxDev < numSATADev; idxDev++) { ASSERT(listSATATest[idxDev] != NULL); sataDiagRandWriteVerify(listSATATest[idxDev], idxPort); } if(numSATADev == 0) { commReportError(MEC_COMP_CODE_TEMP, SATA_DIAG_ITEM_CODE_P0_RAND_WRITE_VERIFY + idxPort, SATA_DIAG_ERROR_CODE_DEVICE_DETECT_FAILED, SATA_DIAG_ERROR_MSG_DEVICE_DETECT_FAILED, SATA_TEST_DEFAULT_SSD_DEVICE, numSATADev, 0, DG_PCH_MSG_FORMAT); } } /******************************************************************************* * PROCEDURE * * NAME: sataPerfromanceTest * SUMMARY: SATA Performance Test * SCOPE: public * * DESCRIPTION: This function peform SATA performance test * * RETURNS: * * NOTES: * */ PUBLIC VOID sataPerformanceTest(UINT8 idxPort) { struct pci_dev* pDev = NULL; struct scsi_device* listSATATest[gMaxSATADeviceNum]; UINT8 idxDev; UINT8 numSATADev; /* Using the vender id and device id of ahci controller to get pci device */ pDev = pci_get_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_PCH, pDev); if(pDev == NULL) { commReportError(MEC_COMP_CODE_TEMP, SATA_DIAG_ITEM_CODE_P0_PERFORMANCE + idxPort, SATA_DIAG_ERROR_CODE_HOST_DETECT_FAILED, SATA_DIAG_ERROR_MSG_HOST_DETECT_FAILED, 0, 0, 0, DG_FORMAT_NONE); return; } /* Perform performance diagnostics on this ahci controller to check if the SATA could reach performance requirement*/ numSATADev = 0; numSATADev = sataGetDeviceList(pDev, idxPort, listSATATest); if(listSATATest[idxDev] == NULL){ commReportError(MEC_COMP_CODE_TEMP, SATA_DIAG_ITEM_CODE_P0_INTERFACE + idxPort, SATA_DIAG_ERROR_CODE_HOST_DETECT_FAILED, SATA_DIAG_ERROR_MSG_HOST_DETECT_FAILED, 0, 0, 0, DG_FORMAT_NONE); return; } for(idxDev = 0; idxDev < numSATADev; idxDev++) { ASSERT(listSATATest[idxDev] != NULL); sataDiagPerformance(listSATATest[idxDev], idxPort); } if(numSATADev == 0) { commReportError(MEC_COMP_CODE_TEMP, SATA_DIAG_ITEM_CODE_P0_PERFORMANCE + idxPort, SATA_DIAG_ERROR_CODE_DEVICE_DETECT_FAILED, SATA_DIAG_ERROR_MSG_DEVICE_DETECT_FAILED, SATA_TEST_DEFAULT_SSD_DEVICE, numSATADev, 0, DG_PCH_MSG_FORMAT); } } /******************************************************************************* * PROCEDURE * * NAME: sataDiagCapacity * SUMMARY: SATA Capacity * SCOPE: public * * DESCRIPTION: This function inquery total sector number from SATA device * and try to read last sector * * RETURNS: * * NOTES: * */ LOCAL VOID sataDiagCapacity(struct scsi_device *pSDev, UINT8 idxPort) { UINT32 sectorTotal; VOID* pReadBuffer = NULL; INT32 rcSATA; /*alloc data buffer for read*/ pReadBuffer = kmalloc(pSDev->sector_size, GFP_KERNEL); if(pReadBuffer == NULL) { commReportError(MEC_COMP_CODE_TEMP, SATA_DIAG_ITEM_CODE_P0_INTERFACE + idxPort, SATA_DIAG_ERROR_CODE_KMALLOC_FAILED, SATA_DIAG_ERROR_MSG_KMALLOC_FAILED, 0, 0, 0, DG_FORMAT_NONE); return; } /*get total sector number form SATA device*/ sectorTotal = sataGetTotalSector(pSDev, SATA_DIAG_ITEM_CODE_P0_INTERFACE + idxPort); /*read last sector from SATA device*/ rcSATA = scsiRead(pSDev, sectorTotal - 1, 1, pReadBuffer); if(rcSATA) { commReportError(MEC_COMP_CODE_TEMP, SATA_DIAG_ITEM_CODE_P0_INTERFACE + idxPort, SATA_DIAG_ERROR_CODE_RD_CMD_EXEC_FAILED, SATA_DIAG_ERROR_MSG_RD_CMD_EXEC_FAILED, (INT64)sectorTotal - 1, 0, (INT64)rcSATA, DG_PCH_IO_FORMAT); return; } kfree(pReadBuffer); } /******************************************************************************* * PROCEDURE * * NAME: sataDiagTransfer * SUMMARY: SATA Transfer Test * SCOPE: public * * DESCRIPTION: This function choose the power of 2 address to * write pre-defined data and read to verify * * RETURNS: * * NOTES: * */ LOCAL VOID sataDiagTransfer(struct scsi_device *pSDev, UINT8 idxPort) { UINT32 offset; UINT32 sectorTest; UINT32 sectorLast; UINT32 sizeTestBuffer; UINT32 lenVer; VOID* pOrigBuffer = NULL; VOID* pWriteBuffer = NULL; VOID* pReadBuffer = NULL; INT32 rcSATA; BOOLEAN hitError = FALSE; sizeTestBuffer = SATA_TRANSFER_TEST_SIZE * pSDev->sector_size; /*alloc data buffer to backup up disk data*/ pOrigBuffer = kmalloc(sizeTestBuffer, GFP_KERNEL); if(pOrigBuffer == NULL) { commReportError(MEC_COMP_CODE_TEMP, SATA_DIAG_ITEM_CODE_P0_TRANSFER + idxPort, SATA_DIAG_ERROR_CODE_KMALLOC_FAILED, SATA_DIAG_ERROR_MSG_KMALLOC_FAILED, 0, 0, 0, DG_FORMAT_NONE); return; } /*alloc data buffer for write*/ pWriteBuffer = kmalloc(sizeTestBuffer, GFP_KERNEL); if(pWriteBuffer == NULL) { commReportError(MEC_COMP_CODE_TEMP, SATA_DIAG_ITEM_CODE_P0_TRANSFER + idxPort, SATA_DIAG_ERROR_CODE_KMALLOC_FAILED, SATA_DIAG_ERROR_MSG_KMALLOC_FAILED, 0, 0, 0, DG_FORMAT_NONE); kfree(pOrigBuffer); return; } /*alloc data buffer for read*/ pReadBuffer = kmalloc(sizeTestBuffer, GFP_KERNEL); if(pReadBuffer == NULL) { commReportError(MEC_COMP_CODE_TEMP, SATA_DIAG_ITEM_CODE_P0_TRANSFER + idxPort, SATA_DIAG_ERROR_CODE_KMALLOC_FAILED, SATA_DIAG_ERROR_MSG_KMALLOC_FAILED, 0, 0, 0, DG_FORMAT_NONE); kfree(pOrigBuffer); kfree(pWriteBuffer); return; } memset(pWriteBuffer, 0xF0, sizeTestBuffer); memset(pReadBuffer, 0x0F, sizeTestBuffer); /*Set up first sector and last sector to perform test*/ sectorLast = sataGetTotalSector(pSDev, SATA_DIAG_ITEM_CODE_P0_TRANSFER + idxPort); sectorTest = SATA_TEST_OFFSET_IN_SECTOR; offset = 1; while(sectorTest <= sectorLast) { /*read original data from SATA device to memory*/ rcSATA = scsiRead(pSDev, sectorTest, SATA_TRANSFER_TEST_SIZE, pOrigBuffer); if(rcSATA) { commReportError(MEC_COMP_CODE_TEMP, SATA_DIAG_ITEM_CODE_P0_TRANSFER + idxPort, SATA_DIAG_ERROR_CODE_RD_CMD_EXEC_FAILED, SATA_DIAG_ERROR_MSG_RD_CMD_EXEC_FAILED, (INT64)sectorTest, 0, (INT64)rcSATA, DG_PCH_IO_FORMAT); break; } /*write test data to SATA device*/ rcSATA = scsiWrite(pSDev, sectorTest, SATA_TRANSFER_TEST_SIZE, pWriteBuffer); if(rcSATA) { commReportError(MEC_COMP_CODE_TEMP, SATA_DIAG_ITEM_CODE_P0_TRANSFER + idxPort, SATA_DIAG_ERROR_CODE_WR_CMD_EXEC_FAILED, SATA_DIAG_ERROR_MSG_WR_CMD_EXEC_FAILED, (INT64)sectorTest, 0, (INT64)rcSATA, DG_PCH_IO_FORMAT); hitError = TRUE; } /*read test data from SATA device*/ rcSATA = scsiRead(pSDev, sectorTest, SATA_TRANSFER_TEST_SIZE, pReadBuffer); if(rcSATA) { commReportError(MEC_COMP_CODE_TEMP, SATA_DIAG_ITEM_CODE_P0_TRANSFER + idxPort, SATA_DIAG_ERROR_CODE_RD_CMD_EXEC_FAILED, SATA_DIAG_ERROR_MSG_RD_CMD_EXEC_FAILED, (INT64)sectorTest, 0, (INT64)rcSATA, DG_PCH_IO_FORMAT); hitError = TRUE; } /*verify data read from SATA device*/ lenVer = sataTransferVerify(pWriteBuffer, pReadBuffer, sizeTestBuffer); if (lenVer){ commReportError(MEC_COMP_CODE_TEMP, SATA_DIAG_ITEM_CODE_P0_TRANSFER + idxPort, SATA_DIAG_ERROR_CODE_DATA_TRANSFER_ERR, SATA_DIAG_ERROR_MSG_DATA_TRANSFER_ERR, (INT64)sectorTest, (INT64)*((UINT32 *)((UINT64)pWriteBuffer + lenVer)), (INT64)*((UINT32 *)((UINT64)pReadBuffer + lenVer)), DG_PCH_IO_FORMAT); hitError = TRUE; } /*recover original data on SATA device*/ rcSATA = scsiWrite(pSDev, sectorTest, SATA_TRANSFER_TEST_SIZE, pOrigBuffer); if(rcSATA) { commReportError(MEC_COMP_CODE_TEMP, SATA_DIAG_ITEM_CODE_P0_TRANSFER + idxPort, SATA_DIAG_ERROR_CODE_WR_CMD_EXEC_FAILED, SATA_DIAG_ERROR_MSG_WR_CMD_EXEC_FAILED, (INT64)sectorTest, 0, (INT64)rcSATA, DG_PCH_IO_FORMAT); break; } memset(pReadBuffer, 0x0F, sizeTestBuffer); /*reach last sector*/ if(hitError || (sectorTest == (sectorLast - 1))) break; /*move to next testing sector*/ sectorTest += offset; offset *= 2; if(sectorTest > sectorLast) sectorTest = sectorLast - 1; } /*Free testing buffer*/ kfree(pOrigBuffer); kfree(pWriteBuffer); kfree(pReadBuffer); } /******************************************************************************* * PROCEDURE * * NAME: sataRWVFunc * SUMMARY: the write/verify operation of the single thread in RandWriteVerify test * SCOPE: public * * DESCRIPTION: This function will write and verify SATA device content * until reach the require command number or hit error * * RETURNS: * * NOTES: * */ static int sataRWVFunc(VOID* data) { SATA_RWVT_THREAD* pRWVThread; struct scsi_device* pSDev; UINT32 offset; UINT32 len; UINT32 lenVer; UINT32 numCmd; INT32 rcSATA; BOOLEAN errHit; BOOLEAN completeTest; /*Initialize parameter for this test*/ pRWVThread = data; pSDev = pRWVThread->pSDev; errHit = FALSE; completeTest = FALSE; numCmd = 0; while (!kthread_should_stop()) { /*Random generate command length and offset*/ len = sataGetRandom() % SATA_RAND_WRITE_VERIFY_TEST_SIZE; if(len == 0) len = SATA_RAND_WRITE_VERIFY_TEST_SIZE; len = len & ~(pSDev->sector_size - 1); offset = SATA_RAND_WRITE_VERIFY_TEST_SIZE - len; offset = offset & ~(pSDev->sector_size - 1); /*Write to SATA device*/ rcSATA = scsiWrite(pSDev, pRWVThread->sectorBase + offset/ pSDev->sector_size, len / pSDev->sector_size, (VOID *)((UINT64)pRWVThread->pSrcBuf + offset)); if(rcSATA) { commReportError(MEC_COMP_CODE_TEMP, SATA_DIAG_ITEM_CODE_P0_RAND_WRITE_VERIFY + pRWVThread->pSATAtest->idxPort, SATA_DIAG_ERROR_CODE_WR_CMD_EXEC_FAILED, SATA_DIAG_ERROR_MSG_WR_CMD_EXEC_FAILED, (INT64)pRWVThread->sectorBase + offset/ pSDev->sector_size, 0, (INT64)rcSATA, DG_PCH_IO_FORMAT); errHit = TRUE; } /*Read from SATA device*/ rcSATA = scsiRead(pSDev, pRWVThread->sectorBase + offset/ pSDev->sector_size, len / pSDev->sector_size, (VOID *)((UINT64)pRWVThread->pDstBuf)); if(rcSATA) { commReportError(MEC_COMP_CODE_TEMP, SATA_DIAG_ITEM_CODE_P0_RAND_WRITE_VERIFY + pRWVThread->pSATAtest->idxPort, SATA_DIAG_ERROR_CODE_RD_CMD_EXEC_FAILED, SATA_DIAG_ERROR_MSG_RD_CMD_EXEC_FAILED, (INT64)pRWVThread->sectorBase + offset/ pSDev->sector_size, 0, (INT64)rcSATA, DG_PCH_IO_FORMAT); errHit = TRUE; } /*Verify content*/ lenVer = sataTransferVerify((VOID *)((UINT64)pRWVThread->pSrcBuf + offset), (VOID *)((UINT64)pRWVThread->pDstBuf), len); if (lenVer){ printk("%u src %x dst %x\n", pRWVThread->idxThread, (UINT32)pRWVThread->pSrcBuf + offset, (UINT32)pRWVThread->pDstBuf); printk("offset %x len %x lexVer %x\n", offset, len, lenVer); commReportError(MEC_COMP_CODE_TEMP, SATA_DIAG_ITEM_CODE_P0_RAND_WRITE_VERIFY + pRWVThread->pSATAtest->idxPort, SATA_DIAG_ERROR_CODE_DATA_TRANSFER_ERR, SATA_DIAG_ERROR_MSG_RAND_WRITE_VERIFY_ERR, (INT64)pRWVThread->sectorBase + offset/ pSDev->sector_size, (INT64)*((UINT32 *)pRWVThread->pSrcBuf + offset + lenVer), (INT64)*((UINT32 *)pRWVThread->pDstBuf + lenVer), DG_PCH_IO_FORMAT); errHit = TRUE; } numCmd++; if((numCmd == SATA_RAND_WRITE_VERIFY_TEST_CMD_NUM) || errHit) { //printk("Thread %u status %u %u\n", pRWVThread->idxThread, errHit, numCmd); break; } if((numCmd % SATA_RAND_WRITE_VERIFY_TEST_RPT_UNIT) == 0) { //printk("Thread %u status %u %u\n", pRWVThread->idxThread, errHit, numCmd); } schedule(); } /*Wake up main thread*/ spin_lock(&pRWVThread->pSATAtest->lock); pRWVThread->isComplete = TRUE; if(atomic_dec_and_test(&pRWVThread->pSATAtest->numThread)) { completeTest = TRUE; } spin_unlock(&pRWVThread->pSATAtest->lock); if(completeTest) wake_up_interruptible(&pRWVThread->pSATAtest->waitqueue); return 0; } /******************************************************************************* * PROCEDURE * * NAME: sataDiagRandWriteVerify * SUMMARY: USB Performance Test * SCOPE: public * * DESCRIPTION: This function peform USB performance test * * RETURNS: * * NOTES: * */ LOCAL VOID sataDiagRandWriteVerify(struct scsi_device *pSDev, UINT8 idxPort) { SATA_RWVT* pRWVTest; SATA_RWVT_THREAD* pRWVThread; UINT32 sectorTest; UINT32 sectorLast; UINT64 addrData; VOID* pDataBuf = NULL; VOID* pTestBuf = NULL; UINT8 idx; UINT8 repeatOP = 0; INT32 rcSATA; UINT32 rptProcess = 0; // The reported progress counter UINT32 curProcess = 0; // The current progress counter /*alloc data buffer for transfering data*/ pTestBuf = kmalloc(SATA_DEVICE_MAX_TRANSFER_SIZE, GFP_KERNEL); if(pTestBuf == NULL) { commReportError(MEC_COMP_CODE_TEMP, SATA_DIAG_ITEM_CODE_P0_RAND_WRITE_VERIFY + idxPort, SATA_DIAG_ERROR_CODE_KMALLOC_FAILED, SATA_DIAG_ERROR_MSG_KMALLOC_FAILED, 0, 0, 0, DG_FORMAT_NONE); return; } /*remap memory region for backup SATA device*/ /*Set the region need to backup and start up copy data from SATA*/ sectorTest = SATA_TEST_OFFSET_IN_SECTOR; sectorLast = sectorTest + SATA_RAND_WRITE_VERIFY_MAX_CMD * SATA_RAND_WRITE_VERIFY_TEST_SIZE / pSDev->sector_size; addrData = gDiagAvailAddr; while(sectorTest < sectorLast) { rcSATA = scsiRead(pSDev, sectorTest, SATA_DEVICE_MAX_TRANSFER_SIZE / pSDev->sector_size, pTestBuf); if(rcSATA) { commReportError(MEC_COMP_CODE_TEMP, SATA_DIAG_ITEM_CODE_P0_RAND_WRITE_VERIFY + idxPort, SATA_DIAG_ERROR_CODE_RD_CMD_EXEC_FAILED, SATA_DIAG_ERROR_MSG_RD_CMD_EXEC_FAILED, (INT64)sectorTest, 0, (INT64)rcSATA, DG_PCH_IO_FORMAT); kfree(pTestBuf); return; } // pDataBuf = ioremap(addrData, SATA_DEVICE_MAX_TRANSFER_SIZE); pDataBuf = kmalloc(SATA_DEVICE_MAX_TRANSFER_SIZE, GFP_KERNEL); if(pDataBuf != NULL) { memcpy(pDataBuf, pTestBuf, SATA_DEVICE_MAX_TRANSFER_SIZE); // iounmap(pDataBuf); kfree( pDataBuf ); } else { commReportError(MEC_COMP_CODE_TEMP, SATA_DIAG_ITEM_CODE_P0_RAND_WRITE_VERIFY + idxPort, SATA_DIAG_ERROR_CODE_KMALLOC_FAILED, SATA_DIAG_ERROR_MSG_KMALLOC_FAILED, 0, 0, 0, DG_FORMAT_NONE); kfree(pTestBuf); return; } sectorTest += SATA_DEVICE_MAX_TRANSFER_SIZE / pSDev->sector_size; addrData += SATA_DEVICE_MAX_TRANSFER_SIZE; M_SATA_REPORT_PROGRESS("SATA RandWriteVerify Test", curProcess, rptProcess, 1); } /*Alloate main thread structure*/ pRWVTest = (SATA_RWVT *)kmalloc(sizeof(SATA_RWVT), GFP_KERNEL); pRWVTest->numThread = (atomic_t)ATOMIC_INIT(0); pRWVTest->idxPort = idxPort; INIT_LIST_HEAD(&pRWVTest->threads); init_waitqueue_head(&pRWVTest->waitqueue); spin_lock_init(&pRWVTest->lock); /*Generate each io thread and set up structure*/ for(idx = 0; idx < SATA_RAND_WRITE_VERIFY_MAX_CMD; idx++) { /*Alloate io thread structure*/ pRWVThread = (SATA_RWVT_THREAD *)kmalloc(sizeof(SATA_RWVT_THREAD), GFP_KERNEL); if (pRWVThread == NULL) { commReportError(MEC_COMP_CODE_TEMP, SATA_DIAG_ITEM_CODE_P0_RAND_WRITE_VERIFY + idxPort, SATA_DIAG_ERROR_CODE_KMALLOC_FAILED, SATA_DIAG_ERROR_MSG_KMALLOC_FAILED, 0, 0, 0, DG_FORMAT_NONE); break; } pRWVThread->pSrcBuf = kmalloc(SATA_RAND_WRITE_VERIFY_TEST_SIZE, GFP_KERNEL); if (pRWVThread->pSrcBuf == NULL) { commReportError(MEC_COMP_CODE_TEMP, SATA_DIAG_ITEM_CODE_P0_RAND_WRITE_VERIFY + idxPort, SATA_DIAG_ERROR_CODE_KMALLOC_FAILED, SATA_DIAG_ERROR_MSG_KMALLOC_FAILED, 0, 0, 0, DG_FORMAT_NONE); kfree(pRWVThread); break; } pRWVThread->pDstBuf = kmalloc(SATA_RAND_WRITE_VERIFY_TEST_SIZE, GFP_KERNEL); if (pRWVThread->pSrcBuf == NULL) { commReportError(MEC_COMP_CODE_TEMP, SATA_DIAG_ITEM_CODE_P0_RAND_WRITE_VERIFY + idxPort, SATA_DIAG_ERROR_CODE_KMALLOC_FAILED, SATA_DIAG_ERROR_MSG_KMALLOC_FAILED, 0, 0, 0, DG_FORMAT_NONE); kfree(pRWVThread); kfree(pRWVThread->pSrcBuf); break; } pRWVThread->pSDev = pSDev; pRWVThread->idxThread = idx; pRWVThread->pSATAtest = pRWVTest; pRWVThread->sectorBase = SATA_TEST_OFFSET_IN_SECTOR + idx * SATA_RAND_WRITE_VERIFY_TEST_SIZE / pSDev->sector_size; smp_wmb(); //printk("thread:%u\n", atomic_read(&pRWVTest->numThread)); //printk("%u src %x dst %x\n", idx, (UINT32)pRWVThread->pSrcBuf, (UINT32)pRWVThread->pDstBuf); pRWVThread->pTask = kthread_run(sataRWVFunc, (VOID *)pRWVThread, "sata-test%u", idx); if (IS_ERR(pRWVThread->pTask)) { commReportError(MEC_COMP_CODE_TEMP, SATA_DIAG_ITEM_CODE_P0_RAND_WRITE_VERIFY + idxPort, SATA_DIAG_ERROR_CODE_KMALLOC_FAILED, SATA_DIAG_ERROR_MSG_KMALLOC_FAILED, 0, 0, 0, DG_FORMAT_NONE); break; } spin_lock(&pRWVTest->lock); pRWVThread->isComplete = FALSE; atomic_inc(&pRWVTest->numThread); spin_unlock(&pRWVTest->lock); list_add_tail(&pRWVThread->node, &pRWVTest->threads); } /*main thread sleep and wait all the io thread complete*/ wait_event_interruptible_timeout(pRWVTest->waitqueue, (atomic_read(&pRWVTest->numThread) == 0), SATA_RAND_WRITE_VERIFY_TEST_TIMEOUT); //printk("thread:%u\n", atomic_read(&pRWVTest->numThread)); M_SATA_REPORT_PROGRESS("SATA RandWriteVerify Test", curProcess, rptProcess, SATA_TEST_DATA_PRESERVE_PRORCESS); sataStopRWVTest(pRWVTest); /*Restore backup data to SATA*/ sectorTest = SATA_TEST_OFFSET_IN_SECTOR; addrData = gDiagAvailAddr; while(sectorTest < sectorLast) { // pDataBuf = ioremap(addrData, SATA_DEVICE_MAX_TRANSFER_SIZE); pDataBuf = kmalloc(SATA_DEVICE_MAX_TRANSFER_SIZE, GFP_KERNEL); if(pDataBuf != NULL) { memcpy(pTestBuf, pDataBuf, SATA_DEVICE_MAX_TRANSFER_SIZE); // iounmap(pDataBuf); kfree( pDataBuf ); } else { commReportError(MEC_COMP_CODE_TEMP, SATA_DIAG_ITEM_CODE_P0_RAND_WRITE_VERIFY + idxPort, SATA_DIAG_ERROR_CODE_KMALLOC_FAILED, SATA_DIAG_ERROR_MSG_KMALLOC_FAILED, 0, 0, 0, DG_FORMAT_NONE); if(repeatOP++ < SATA_TEST_MAX_REPEAT_TIME) continue; else break; } rcSATA = scsiWrite(pSDev, sectorTest, SATA_DEVICE_MAX_TRANSFER_SIZE / pSDev->sector_size, pTestBuf); if(rcSATA) { commReportError(MEC_COMP_CODE_TEMP, SATA_DIAG_ITEM_CODE_P0_RAND_WRITE_VERIFY + idxPort, SATA_DIAG_ERROR_CODE_WR_CMD_EXEC_FAILED, SATA_DIAG_ERROR_MSG_WR_CMD_EXEC_FAILED, (INT64)sectorTest, 0, (INT64)rcSATA, DG_PCH_IO_FORMAT); if(repeatOP++ < SATA_TEST_MAX_REPEAT_TIME) continue; else break; } sectorTest += SATA_DEVICE_MAX_TRANSFER_SIZE / pSDev->sector_size; addrData += SATA_DEVICE_MAX_TRANSFER_SIZE; M_SATA_REPORT_PROGRESS("SATA RandWriteVerify Test", curProcess, rptProcess, 1); } /*Free resource*/ kfree(pRWVTest); kfree(pTestBuf); M_SATA_REPORT_PROGRESS("SATA RandWriteVerify Test", curProcess, rptProcess, 0); } /******************************************************************************* * PROCEDURE * * NAME: sataStopRWVTest * SUMMARY: USB Performance Test * SCOPE: public * * DESCRIPTION: This function peform USB performance test * * RETURNS: * * NOTES: * */ LOCAL VOID sataStopRWVTest(SATA_RWVT* pRWVTest) { SATA_RWVT_THREAD* pRWVThread; SATA_RWVT_THREAD* _pRWVThread; INT32 rc; spin_lock(&pRWVTest->lock); list_for_each_entry_safe(pRWVThread, _pRWVThread, &pRWVTest->threads, node) { if(!pRWVThread->isComplete) { printk("stop thread %u\n", pRWVThread->idxThread); rc = kthread_stop(pRWVThread->pTask); } list_del(&pRWVThread->node); kfree(pRWVThread->pSrcBuf); kfree(pRWVThread->pDstBuf); kfree(pRWVThread); } spin_unlock(&pRWVTest->lock); } /******************************************************************************* * PROCEDURE * * NAME: sataDiagPerformance * SUMMARY: SATA Performance Test * SCOPE: public * * DESCRIPTION: This function write 100MB data to SATA sequantially and calculate preformance * * RETURNS: * * NOTES: * */ LOCAL VOID sataDiagPerformance(struct scsi_device *pSDev, UINT8 idxPort) { UINT32 sectorTest; UINT32 sectorLast; UINT8 idxTest; VOID* pTestBuf; VOID* pDataBuf; UINT64 addrData; UINT8 repeatOP = 0; unsigned long startTestTime; unsigned long totalTestTime; INT32 rcSATA; UINT32 rptProcess = 0; // The reported progress counter UINT32 curProcess = 0; // The current progress counter UINT32 rptPoint = 0; totalTestTime = 0; /*alloc buffer for transfer*/ pTestBuf = kmalloc(SATA_DEVICE_MAX_TRANSFER_SIZE, GFP_KERNEL); if(pTestBuf == NULL) { commReportError(MEC_COMP_CODE_TEMP, SATA_DIAG_ITEM_CODE_P0_PERFORMANCE + idxPort, SATA_DIAG_ERROR_CODE_KMALLOC_FAILED, SATA_DIAG_ERROR_MSG_KMALLOC_FAILED, 0, 0, 0, DG_FORMAT_NONE); return; } sectorTest = SATA_TEST_OFFSET_IN_SECTOR; sectorLast = sectorTest + SATA_DEVICE_PERFORMANCE_TEST_SIZE / pSDev->sector_size; addrData = gDiagAvailAddr; rptPoint = SATA_DEVICE_PERFORMANCE_RPT_VALUE; while(sectorTest != sectorLast) { rcSATA = scsiRead(pSDev, sectorTest, SATA_DEVICE_MAX_TRANSFER_SIZE / pSDev->sector_size, pTestBuf); if(rcSATA) { commReportError(MEC_COMP_CODE_TEMP, SATA_DIAG_ITEM_CODE_P0_PERFORMANCE + idxPort, SATA_DIAG_ERROR_CODE_RD_CMD_EXEC_FAILED, SATA_DIAG_ERROR_MSG_RD_CMD_EXEC_FAILED, (INT64)sectorTest, 0, (INT64)rcSATA, DG_PCH_IO_FORMAT); kfree(pTestBuf); return; } /*remap memory region for backup SATA device*/ // pDataBuf = ioremap(addrData, SATA_DEVICE_MAX_TRANSFER_SIZE); pDataBuf = kmalloc( SATA_DEVICE_MAX_TRANSFER_SIZE, GFP_KERNEL ); if(pDataBuf != NULL) { memcpy(pDataBuf, pTestBuf, SATA_DEVICE_MAX_TRANSFER_SIZE); // iounmap(pDataBuf); kfree(pDataBuf); } else { commReportError(MEC_COMP_CODE_TEMP, SATA_DIAG_ITEM_CODE_P0_PERFORMANCE + idxPort, SATA_DIAG_ERROR_CODE_KMALLOC_FAILED, SATA_DIAG_ERROR_MSG_KMALLOC_FAILED, 0, 0, 0, DG_FORMAT_NONE); kfree(pTestBuf); return; } sectorTest += SATA_DEVICE_MAX_TRANSFER_SIZE / pSDev->sector_size; addrData += SATA_DEVICE_MAX_TRANSFER_SIZE; if(rptPoint-- == 0) { M_SATA_REPORT_PROGRESS("SATA Performance Test", curProcess, rptProcess, 1); rptPoint = SATA_DEVICE_PERFORMANCE_RPT_VALUE; } } /*Write 100MB data to flash device*/ for(idxTest = 0; idxTest < (SATA_DEVICE_PERFORMANCE_TEST_ROUND + SATA_DEVICE_PERFORMANCE_TEST_RAMP_UP); idxTest++) { sectorTest = SATA_TEST_OFFSET_IN_SECTOR; while(sectorTest < sectorLast) { startTestTime = jiffies; rcSATA = scsiWrite(pSDev, sectorTest, SATA_DEVICE_MAX_TRANSFER_SIZE / pSDev->sector_size, pTestBuf); if(rcSATA) { commReportError(MEC_COMP_CODE_TEMP, SATA_DIAG_ITEM_CODE_P0_PERFORMANCE + idxPort, SATA_DIAG_ERROR_CODE_WR_CMD_EXEC_FAILED, SATA_DIAG_ERROR_MSG_WR_CMD_EXEC_FAILED, (INT64)sectorTest, 0, (INT64)rcSATA, DG_PCH_IO_FORMAT); totalTestTime = 0; break; } totalTestTime += jiffies - startTestTime; sectorTest += SATA_DEVICE_MAX_TRANSFER_SIZE / pSDev->sector_size; } if(idxTest == 0) totalTestTime = 0; //skip first round data M_SATA_REPORT_PROGRESS("SATA Performance Test", curProcess, rptProcess, SATA_TEST_PERFORMANCE_UNIT_PRORCESS); } totalTestTime /= SATA_DEVICE_PERFORMANCE_TEST_ROUND; if (totalTestTime != 0) { /*Calculate write performance*/ printk("[Port %d]SSD Performance:%lu.%lu MB/s\n", idxPort, SATA_DEVICE_PERFORMANCE_TEST_SIZE / MB * HZ / totalTestTime, SATA_DEVICE_PERFORMANCE_TEST_SIZE / MB * HZ % totalTestTime); if((SATA_DEVICE_PERFORMANCE_TEST_SIZE / MB * HZ / totalTestTime) < SATA_DEVICE_PERFORMANCE_TEST_TARGET ) { commReportError(MEC_COMP_CODE_TEMP, SATA_DIAG_ITEM_CODE_P0_PERFORMANCE + idxPort, SATA_DIAG_ERROR_CODE_PERFORMANCE_FAILED, SATA_DIAG_ERROR_MSG_PERFORMANCE_FAILED, SATA_DEVICE_PERFORMANCE_TEST_TARGET, (INT64)((100 * HZ) / totalTestTime), 0, DG_PCH_MSG_FORMAT); } } sectorTest = SATA_TEST_OFFSET_IN_SECTOR; addrData = gDiagAvailAddr; rptPoint = SATA_DEVICE_PERFORMANCE_RPT_VALUE; while(sectorTest != sectorLast) { /*remap memory region for backup SATA device*/ //pDataBuf = ioremap(addrData, SATA_DEVICE_MAX_TRANSFER_SIZE); pDataBuf = kmalloc( SATA_DEVICE_MAX_TRANSFER_SIZE, GFP_KERNEL ); if(pDataBuf != NULL) { memcpy(pTestBuf, pDataBuf, SATA_DEVICE_MAX_TRANSFER_SIZE); // iounmap(pDataBuf); kfree(pDataBuf); } else { commReportError(MEC_COMP_CODE_TEMP, SATA_DIAG_ITEM_CODE_P0_PERFORMANCE + idxPort, SATA_DIAG_ERROR_CODE_KMALLOC_FAILED, SATA_DIAG_ERROR_MSG_KMALLOC_FAILED, 0, 0, 0, DG_FORMAT_NONE); if(repeatOP++ < SATA_TEST_MAX_REPEAT_TIME) continue; else break; } rcSATA = scsiWrite(pSDev, sectorTest, SATA_DEVICE_MAX_TRANSFER_SIZE / pSDev->sector_size, pTestBuf); if(rcSATA) { commReportError(MEC_COMP_CODE_TEMP, SATA_DIAG_ITEM_CODE_P0_PERFORMANCE + idxPort, SATA_DIAG_ERROR_CODE_WR_CMD_EXEC_FAILED, SATA_DIAG_ERROR_MSG_WR_CMD_EXEC_FAILED, (INT64)sectorTest, 0, (INT64)rcSATA, DG_PCH_IO_FORMAT); if(repeatOP++ < SATA_TEST_MAX_REPEAT_TIME) continue; else break; } sectorTest += SATA_DEVICE_MAX_TRANSFER_SIZE / pSDev->sector_size; addrData += SATA_DEVICE_MAX_TRANSFER_SIZE; if(rptPoint-- == 0) { M_SATA_REPORT_PROGRESS("SATA Performance Test", curProcess, rptProcess, 1); rptPoint = SATA_DEVICE_PERFORMANCE_RPT_VALUE; } } M_SATA_REPORT_PROGRESS("SATA Performance Test", curProcess, rptProcess, 0); kfree(pTestBuf); } /******************************************************************************* * PROCEDURE * * NAME: sataTransferVerify * SUMMARY: Verify SATA transfer result * SCOPE: local * * DESCRIPTION: This function verify the content that transfered by SATA * * RETURNS: * * NOTES: * */ LOCAL UINT32 sataTransferVerify(VOID* pSrcBuf, VOID* pDstBuf, UINT32 length) { UINT8* pVerifySrc; UINT8* pVerifyDest; pVerifySrc = pSrcBuf; pVerifyDest = pDstBuf; while(length) { if( (length >= sizeof(UINT32)) && ((UINT64)pVerifySrc % sizeof(UINT32)) == 0 ) { if(*((UINT32 *)pVerifySrc) != *((UINT32 *)pVerifyDest) ) { //pSrcBuf = (VOID *)pVerifySrc; //pDstBuf = (VOID *)pVerifyDest; return length; } pVerifySrc+=sizeof(UINT32); pVerifyDest+=sizeof(UINT32); length-=sizeof(UINT32); } else { if(*pVerifySrc != *pVerifyDest ) { //pSrcBuf = (VOID *)pVerifySrc; //pDstBuf = (VOID *)pVerifyDest; return length; } pVerifySrc++; pVerifyDest++; length--; } } return length; } /******************************************************************************* * PROCEDURE * * NAME: sataGetRandom * SUMMARY: Get random generated number * SCOPE: local * * DESCRIPTION: This function generate random number * * RETURNS: * * NOTES: * */ LOCAL UINT16 sataGetRandom(VOID) { UINT16 random; get_random_bytes(&random, sizeof(random)); return random; } #if 0 /******************************************************************************* * PROCEDURE * * NAME: sataGetRandom * SUMMARY: Verify SATA transfer result * SCOPE: local * * DESCRIPTION: This function verify the content that transfered by USB * * RETURNS: * * NOTES: * */ LOCAL VOID sataTransferSizeTest(struct scsi_device *pSDev) { VOID* pTestBuf; UINT32 testSize; INT32 rcSATA; pTestBuf = kmalloc(SATA_DEVICE_MAX_TRANSFER_SIZE, GFP_KERNEL); if(pTestBuf == NULL) { printk("kmalloc failed\n"); } testSize = 1; while(testSize < SATA_DEVICE_MAX_TRANSFER_SIZE){ printk("read size at %u\n", testSize); rcSATA = scsiRead(pSDev, SATA_TEST_OFFSET_IN_SECTOR, testSize/pSDev->sector_size, pTestBuf); if(rcSATA) { break; } testSize*=2; } printk("Max read size %u\n", testSize); testSize = 1; while(testSize < SATA_DEVICE_MAX_TRANSFER_SIZE){ printk("write size at %u\n", testSize); rcSATA = scsiWrite(pSDev, SATA_TEST_OFFSET_IN_SECTOR, testSize/pSDev->sector_size, pTestBuf); if(rcSATA) { break; } testSize*=2; } printk("Max write size %u\n", testSize); kfree(pTestBuf); } /******************************************************************************* * PROCEDURE * * NAME: sataGetCapacity * SUMMARY: USB Performance Test * SCOPE: public * * DESCRIPTION: This function peform USB performance test * * RETURNS: * * NOTES: * */ LOCAL UINT32 sataGetCapacity(struct scsi_device *pSDev) { UINT8 buffer[16]; INT32 rcSATA; INT32 sizeSector = 0; sector_t numTotalSector = 0; ASSERT(pSDev != NULL); /*read capacity data from usb device*/ rcSATA = scsiReadCapacity(pSDev, buffer); if(rcSATA) { commReportError(MEC_COMP_CODE_TEMP, SATA_DIAG_ITEM_CODE_INTERFACE, SATA_DIAG_ERROR_CODE_RD_CMD_EXEC_FAILED, SATA_DIAG_ERROR_MSG_RD_CMD_EXEC_FAILED, 0, 0, 0, DG_FORMAT_NONE); } /*compare capacity data read from usb device with usb information*/ sizeSector = (buffer[4] << 24) | (buffer[5] << 16) | (buffer[6] << 8) | buffer[7]; numTotalSector = (((sector_t)buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]) + 1; return (sizeSector * numTotalSector); } #endif /******************************************************************************* * PROCEDURE * * NAME: sataGetTotalSector * SUMMARY: USB Performance Test * SCOPE: public * * DESCRIPTION: This function peform USB performance test * * RETURNS: * * NOTES: * */ LOCAL UINT32 sataGetTotalSector(struct scsi_device *pSDev, INT8 itemCode) { UINT8 buffer[16]; INT32 rcSATA; sector_t numTotalSector = 0; ASSERT(pSDev != NULL); /*read capacity data from usb device*/ rcSATA = scsiReadCapacity(pSDev, buffer); if(rcSATA) { commReportError(MEC_COMP_CODE_TEMP, itemCode, SATA_DIAG_ERROR_CODE_RD_CMD_EXEC_FAILED, SATA_DIAG_ERROR_MSG_RD_CMD_EXEC_FAILED, 0, 0, 0, DG_FORMAT_NONE); return 0; } /*compare capacity data read from usb device with usb information*/ numTotalSector = (((sector_t)buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]) + 1; return (UINT32)numTotalSector; } MODULE_AUTHOR( "will.lee@netapp.com" ); MODULE_DESCRIPTION( "NetApp SATA Diag Code" ); MODULE_LICENSE( "GPL" ); MODULE_VERSION( "0.1" );