#include #include #include #include #include #include #include #include #include #include //#include #include #include #include #include "../mpt3sas_base.h" #include "inc/diag_mpt.h" #include "inc/sasdiag.h" #include "inc/diag_debug.h" /* * Extern Declaration */ extern void generateDataPatterns(U8 pat, void *buf, U32 len); extern struct MPT3SAS_ADAPTER *_scsih_get_ioc_address(int ioc_id); extern bool isIocNumValid(U8 ioc_num); extern void _base_add_sg_single_ieee(void *paddr, u8 flags, u8 chain_offset, u32 length, dma_addr_t dma_addr); extern void _base_build_zero_len_sge_ieee(struct MPT3SAS_ADAPTER *ioc, void *paddr); //static DECLARE_WAIT_QUEUE_HEAD(ctl_poll_wait); enum io_state { NON_BLOCKING, BLOCKING, }; /** * */ static long sasdiag_sendScsiCdb(struct MPT3SAS_ADAPTER *ioc, U16 handle, U8* CDB, U8 CDBLen, U32 inSize, void *dataInBuf, U32 outSize, void *dataOutBuf) { MPI2RequestHeader_t *mpi_request = NULL, *request; MPI2DefaultReply_t *mpi_reply; u8 issue_reset = 0; u16 ioc_status, smid, wait_state_count; u32 ioc_state, sgl_flags, request_size, sge_offset; unsigned long timeout, timeleft; void *psge, *data_out = NULL, *data_in = NULL; dma_addr_t data_out_dma = 0, data_in_dma = 0; size_t data_out_sz = 0, data_in_sz = 0; long ret = 0; Mpi2SCSIIORequest_t temp, *tempPtr; enum io_state state = BLOCKING; if (state == NON_BLOCKING && !mutex_trylock(&ioc->ctl_cmds.mutex)) return -EAGAIN; else if (mutex_lock_interruptible(&ioc->ctl_cmds.mutex)) return -ERESTARTSYS; if (ioc->ctl_cmds.status != MPT3_CMD_NOT_USED) { printk(MPT3SAS_ERR_FMT "%s: ctl_cmd in use\n", ioc->name, __func__); ret = -EAGAIN; goto out; } wait_state_count = 0; ioc_state = mpt3sas_base_get_iocstate(ioc, 1); while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { if (wait_state_count++ == 10) { printk(MPT3SAS_ERR_FMT "%s: failed due to ioc not operational\n", ioc->name, __func__); ret = -EFAULT; goto out; } ssleep(1); ioc_state = mpt3sas_base_get_iocstate(ioc, 1); printk(MPT3SAS_INFO_FMT "%s: waiting for " "operational state(count=%d)\n", ioc->name, __func__, wait_state_count); } if (wait_state_count) printk(MPT3SAS_INFO_FMT "%s: ioc is operational\n", ioc->name, __func__); // Build the msg frame mpi_request = kzalloc(ioc->request_sz, GFP_KERNEL); if (!mpi_request) { printk(MPT3SAS_ERR_FMT "%s: failed obtaining a memory for " "mpi_request\n", ioc->name, __func__); ret = -ENOMEM; goto out; } smid = mpt3sas_base_get_smid_scsiio(ioc, ioc->ctl_cb_idx, NULL); if (!smid) { printk(MPT3SAS_ERR_FMT "%s: failed obtaining a smid\n", ioc->name, __func__); ret = -EAGAIN; goto out; } ioc->ctl_cmds.smid = smid; ioc->ctl_cmds.status = MPT3_CMD_PENDING; memset(ioc->ctl_cmds.reply, 0, ioc->reply_sz); request = mpt3sas_base_get_msg_frame(ioc, smid); data_out_sz = outSize; data_in_sz = inSize; // Fill in MPT request, for now just handle SCSIIO type request_size = sizeof(temp) - sizeof(temp.SGL); sge_offset = request_size / 4; tempPtr = (Mpi2SCSIIORequest_t *)mpi_request; memset(tempPtr, 0, sizeof(Mpi2SCSIIORequest_t)); mpi_request->Function = MPI2_FUNCTION_SCSI_IO_REQUEST; mpi_request->FunctionDependent1 = handle; if (mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST || mpi_request->Function == MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH) { if (!le16_to_cpu(mpi_request->FunctionDependent1) || le16_to_cpu(mpi_request->FunctionDependent1) > ioc->facts.MaxDevHandle) { DBG_PRINT(DBG_ERR, "sendScsiCdb: IOC %d cmd 0x%02x - parm exceed IOC limit\n", ioc->id, CDB[0]); ret = -EINVAL; mpt3sas_base_free_smid(ioc, smid); goto out; } if (data_in_sz) tempPtr->Control = __cpu_to_le32(MPI2_SCSIIO_CONTROL_READ); else if (data_out_sz) tempPtr->Control = __cpu_to_le32(MPI2_SCSIIO_CONTROL_WRITE); else tempPtr->Control = __cpu_to_le32(MPI2_SCSIIO_CONTROL_NODATATRANSFER); tempPtr->DevHandle = handle; tempPtr->LUN[1] = 0; tempPtr->IoFlags = __cpu_to_le16(CDBLen) & MPI2_SCSIIO_IOFLAGS_CDBLENGTH_MASK; tempPtr->DataLength = __cpu_to_le32(inSize); tempPtr->SGLOffset0 = offsetof(Mpi2SCSIIORequest_t, SGL) / 4; memcpy(&tempPtr->CDB.CDB32[0], CDB, CDBLen); // Copy data to msg frame pointed by smid memcpy(request, mpi_request, sge_offset * 4); } /* obtain dma-able memory for data transfer */ if (data_out_sz) /* WRITE */ { data_out = pci_alloc_consistent(ioc->pdev, data_out_sz, &data_out_dma); if (!data_out) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); ret = -ENOMEM; mpt3sas_base_free_smid(ioc, smid); goto out; } // Copy write buffer from caller memcpy(data_out, dataOutBuf, data_out_sz); } if (data_in_sz) /* READ */ { data_in = pci_alloc_consistent(ioc->pdev, data_in_sz, &data_in_dma); if (!data_in) { printk(KERN_ERR "failure at %s:%d/%s()!\n", __FILE__, __LINE__, __func__); ret = -ENOMEM; mpt3sas_base_free_smid(ioc, smid); goto out; } } /* add scatter gather elements */ psge = (void *)request + (sge_offset*4); if (!data_out_sz && !data_in_sz) { _base_build_zero_len_sge_ieee(ioc, psge); } if (data_out_sz && data_in_sz) { /* WRITE sgel first */ sgl_flags = MPI2_IEEE_SGE_FLAGS_SIMPLE_ELEMENT | MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR; _base_add_sg_single_ieee(psge, sgl_flags, 0, data_out_sz, data_out_dma); /* incr sgel */ psge += ioc->sge_size_ieee; /* READ sgel last */ sgl_flags |= MPI25_IEEE_SGE_FLAGS_END_OF_LIST; _base_add_sg_single_ieee(psge, sgl_flags, 0, data_in_sz, data_in_dma); } else if (data_out_sz) /* WRITE */ { sgl_flags = MPI2_IEEE_SGE_FLAGS_SIMPLE_ELEMENT | MPI25_IEEE_SGE_FLAGS_END_OF_LIST | MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR; _base_add_sg_single_ieee(psge, sgl_flags, 0, data_out_sz, data_out_dma); } else if (data_in_sz) /* READ */ { sgl_flags = MPI2_IEEE_SGE_FLAGS_SIMPLE_ELEMENT | MPI25_IEEE_SGE_FLAGS_END_OF_LIST | MPI2_IEEE_SGE_FLAGS_SYSTEM_ADDR; _base_add_sg_single_ieee(psge, sgl_flags, 0, data_in_sz, data_in_dma); } /* send command to firmware */ switch (mpi_request->Function) { case MPI2_FUNCTION_SCSI_IO_REQUEST: case MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH: { Mpi2SCSIIORequest_t *scsiio_request = (Mpi2SCSIIORequest_t *)request; scsiio_request->SenseBufferLength = SCSI_SENSE_BUFFERSIZE; scsiio_request->SenseBufferLowAddress = mpt3sas_base_get_sense_buffer_dma(ioc, smid); memset(ioc->ctl_cmds.sense, 0, SCSI_SENSE_BUFFERSIZE); if (mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST) ioc->put_smid_scsi_io(ioc, smid, le16_to_cpu(mpi_request->FunctionDependent1)); else ioc->put_smid_default(ioc, smid); break; } case MPI2_FUNCTION_SMP_PASSTHROUGH: { Mpi2SmpPassthroughRequest_t *smp_request = (Mpi2SmpPassthroughRequest_t *)mpi_request; u8 *data; /* ioc determines which port to use */ smp_request->PhysicalPort = 0xFF; if (smp_request->PassthroughFlags & MPI2_SMP_PT_REQ_PT_FLAGS_IMMEDIATE) data = (u8 *)&smp_request->SGL; else data = data_out; if (data[1] == 0x91 && (data[10] == 1 || data[10] == 2)) { ioc->ioc_link_reset_in_progress = 1; ioc->ignore_loginfos = 1; } ioc->put_smid_default(ioc, smid); break; } case MPI2_FUNCTION_SAS_IO_UNIT_CONTROL: { Mpi2SasIoUnitControlRequest_t *sasiounit_request = (Mpi2SasIoUnitControlRequest_t *)mpi_request; if (sasiounit_request->Operation == MPI2_SAS_OP_PHY_HARD_RESET || sasiounit_request->Operation == MPI2_SAS_OP_PHY_LINK_RESET) { ioc->ioc_link_reset_in_progress = 1; ioc->ignore_loginfos = 1; } ioc->put_smid_default(ioc, smid); break; } default: ioc->put_smid_default(ioc, smid); break; } // force timeout at 10 sec timeout = 10; init_completion(&ioc->ctl_cmds.done); timeleft = wait_for_completion_timeout(&ioc->ctl_cmds.done, timeout*HZ); if ((mpi_request->Function == MPI2_FUNCTION_SMP_PASSTHROUGH || mpi_request->Function == MPI2_FUNCTION_SAS_IO_UNIT_CONTROL) && ioc->ioc_link_reset_in_progress) { ioc->ioc_link_reset_in_progress = 0; ioc->ignore_loginfos = 0; } if (!(ioc->ctl_cmds.status & MPT3_CMD_COMPLETE)) { printk(MPT3SAS_ERR_FMT "%s: timeout\n", ioc->name, __func__); _debug_dump_mf(mpi_request, sge_offset); if (!(ioc->ctl_cmds.status & MPT3_CMD_RESET)) issue_reset = 1; goto issue_host_reset; } mpi_reply = ioc->ctl_cmds.reply; ioc_status = le16_to_cpu(mpi_reply->IOCStatus) & MPI2_IOCSTATUS_MASK; /* copy read xdata to caller */ if (data_in_sz) memcpy(dataInBuf, data_in, data_in_sz); issue_host_reset: if (issue_reset) { ret = -ENODATA; if ((mpi_request->Function == MPI2_FUNCTION_SCSI_IO_REQUEST || mpi_request->Function == MPI2_FUNCTION_RAID_SCSI_IO_PASSTHROUGH)) { printk(MPT3SAS_INFO_FMT "issue target reset: handle " "= (0x%04x)\n", ioc->name, le16_to_cpu(mpi_request->FunctionDependent1)); mpt3sas_halt_firmware(ioc, 0); mpt3sas_scsih_issue_tm(ioc, le16_to_cpu(mpi_request->FunctionDependent1), 0, 0, 0, MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET, 0, 30, 0, 1); } else mpt3sas_base_hard_reset_handler(ioc, FORCE_BIG_HAMMER); } out: /* free memory associated with sg buffers */ if (data_in) pci_free_consistent(ioc->pdev, data_in_sz, data_in, data_in_dma); if (data_out) pci_free_consistent(ioc->pdev, data_out_sz, data_out, data_out_dma); if (mpi_request) kfree(mpi_request); ioc->ctl_cmds.status = MPT3_CMD_NOT_USED; mutex_unlock(&ioc->ctl_cmds.mutex); return ret; } // // Issue SCSI SPC commands (not block commands) // int sasdiag_sendScsiCmd(U8 ioc_num, U8 cmdCode, U16 devHandle, U8 pat, U32 loopCount, void *data) { U8 CDBLength = 0; U8 CDB[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; int i = 0, numRetry, ret = 0; struct MPT3SAS_ADAPTER *ioc = NULL; void *inBuf = NULL, *outBuf = NULL; U32 inSize = 0, outSize = 0; DBG_PRINT(DBG_IO, "sendScsiCmd: IOC %d cmdCode 0x%02x \n", ioc_num, cmdCode); if (!isIocNumValid(ioc_num)) { DBG_PRINT(DBG_ERR, "sendScsiCmd: FAILED - IOC %d invalid\n", ioc_num); return -1; } ioc = _scsih_get_ioc_address(ioc_num); if (ioc == NULL){ DBG_PRINT(DBG_ERR, "sendScsiCmd: FAILED - IOC %d can't get ioc struct\n", ioc_num); return -1; } DBG_PRINT(DBG_IO, "sendScsiCmd: IOC %d send cmd 0x%02x to devHandle 0x%04x " "pat %d loopCount %d\n", ioc_num, cmdCode, devHandle, pat, loopCount); switch (cmdCode) { case SCSI_COMMAND_INQUIRY: { inSize = sizeof(INQUIRY_DATA); CDBLength = 6; CDB[0] = cmdCode; CDB[4] = inSize; inBuf = (char *)kmalloc(inSize, GFP_KERNEL); break; } case SCSI_COMMAND_TEST_UNIT_READY: { CDBLength = 6; CDB[0] = cmdCode; break; } case SCSI_COMMAND_READ_CAPACITY: { inSize = 8; CDBLength = 10; CDB[0] = cmdCode; inBuf = (char *)kmalloc(inSize, GFP_KERNEL); break; } case SCSI_COMMAND_READ_CAPACITY_16: { inSize = 32; CDBLength = 16; CDB[0] = cmdCode; CDB[1] = 0x10; CDB[10] = inSize >> 24; CDB[11] = inSize >> 16; CDB[12] = inSize >> 8; CDB[13] = inSize; inBuf = (char *)kmalloc(inSize, GFP_KERNEL); break; } case SCSI_COMMAND_READ_BUFFER: { inSize = 1024; CDBLength = 10; CDB[0] = cmdCode; CDB[1] = 0x02;; // for now data mode CDB[6] = inSize >> 16; CDB[7] = inSize >> 8; CDB[8] = inSize; inBuf = (char *)kmalloc(inSize, GFP_KERNEL); break; } case SCSI_COMMAND_WRITE_BUFFER: { outSize = 1024; // for now CDBLength = 10; CDB[0] = cmdCode; CDB[1] = 0x02;; // for now data mode CDB[6] = outSize >> 16; CDB[7] = outSize >> 8; CDB[8] = outSize; outBuf = (char *)kmalloc(outSize, GFP_KERNEL); break; } default: break; } // switch // If write data out, check for required data pattern if (outBuf) { if (pat) generateDataPatterns(pat, outBuf, outSize); else memset(outBuf, 0, outSize); // no pat required, clear buf } // Clear the buffer for data read if (inBuf) memset(inBuf, 0, inSize); for (i = 0; i < loopCount; i++) { numRetry = 2; do { ret = sasdiag_sendScsiCdb(ioc, devHandle, &CDB[0], CDBLength, inSize, inBuf, outSize, outBuf); } while ((ret == -EAGAIN) && (--numRetry > 0)); } // for if (ret) { DBG_PRINT(DBG_ERR, "sendScsiCmd: IOC %d cmd 0x%02x FAILED err %d (loop %d)\n", ioc_num, cmdCode, ret, i); goto out; } else { DBG_PRINT(DBG_IO, "sendScsiCmd: IOC %d cmd 0x%02x Successful all %d loops\n", ioc_num, cmdCode, loopCount); } // if caller wants data return if ((data != NULL) && (inBuf)) memcpy((char *)data, inBuf, inSize); // Successful, print data if (!(g_sasdiag_dbg_flag & DBG_RDDATA) && !(g_sasdiag_dbg_flag & DBG_RDDATA)) goto out; switch (cmdCode) { case SCSI_COMMAND_INQUIRY: { DBG_PRINT(DBG_RDDATA, "\nDEVHANDLE 0x%04x INQUIRY DATA:", devHandle); DBG_PRINT(DBG_RDDATA, "\n==============================\n"); break; } case SCSI_COMMAND_READ_CAPACITY: case SCSI_COMMAND_READ_CAPACITY_16: DBG_PRINT(DBG_RDDATA, "\nDEVHANDLE 0x%04x READ CAPACITY:", devHandle); DBG_PRINT(DBG_RDDATA, "\n==============================\n"); break; case SCSI_COMMAND_READ_BUFFER: DBG_PRINT(DBG_RDDATA, "\nDEVHANDLE 0x%04x READ BUFFER:", devHandle); DBG_PRINT(DBG_RDDATA, "\n=============================\n"); break; case SCSI_COMMAND_WRITE_BUFFER: DBG_PRINT(DBG_WRDATA, "\nDEVHANDLE 0x%04x WRITE BUFFER:", devHandle); DBG_PRINT(DBG_WRDATA, "\n=============================\n"); break; default: break; } // switch out: if(inBuf) kfree(inBuf); if(outBuf) kfree(outBuf); return ret; } // // Issue SCSI Block commands (not SPC commands) // If ioSizeInBlock is 0, the default size is used (1KB) // int sasdiag_sendBlockCmd(U8 ioc_num, U8 cmdCode, U16 devHandle, U16 ioSizeInBlocks, U32 lba, void *inBuf, U32 inSize, void *outBuf, U32 outSize, U8 pat, U32 loopCount) { U8 CDBLength = 0; U8 CDB[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; int i = 0, numRetry, ret = 0; struct MPT3SAS_ADAPTER *ioc = NULL; U32 eedpMode = 0; #if defined(ARAPAHO_BOARD) U32 *buf1; #endif DBG_PRINT(DBG_IO, "sendBlockCmd: IOC %d cmdCode 0x%02x \n", ioc_num, cmdCode); if (!isIocNumValid(ioc_num)) { DBG_PRINT(DBG_ERR, "sendBlockCmd: FAILED - IOC %d invalid\n", ioc_num); return -1; } ioc = _scsih_get_ioc_address(ioc_num); if (ioc == NULL){ DBG_PRINT(DBG_ERR, "sendBlockCmd: FAILED - IOC %d can't get ioc struct\n", ioc_num); return -1; } // For now limit to 64blocks (64 * 512 = 32KB) IO size if (ioSizeInBlocks > 64) ioSizeInBlocks = 64; DBG_PRINT(DBG_IO, "sendBlockCmd: IOC %d send cmd 0x%02x to devHandle 0x%04x " "ioSizeInBocks %d lba 0x%08x pat %d loopCount %d\n", ioc_num, cmdCode, devHandle, ioSizeInBlocks, lba, pat, loopCount); switch (cmdCode) { case SCSI_COMMAND_READ_10: case SCSI_COMMAND_WRITE_10: CDBLength = 10; CDB[0] = cmdCode; CDB[1] = eedpMode & 0xF0; CDB[2] = lba >> 24; CDB[3] = lba >> 16; CDB[4] = lba >> 8; CDB[5] = lba; CDB[7] = ioSizeInBlocks >> 8; CDB[8] = ioSizeInBlocks; break; case SCSI_COMMAND_READ_16: case SCSI_COMMAND_WRITE_16: default: DBG_PRINT(DBG_IO, "sendBlockCmd: IOC %d cmd 0x%02x not supported\n", ioc_num, cmdCode); break; } // switch // If write data out, check for required data pattern if (outBuf) { if (pat) generateDataPatterns(pat, outBuf, outSize); else memset(outBuf, 0, outSize); // no pat required, clear buf } // Clear the buffer for data read if (inBuf) memset(inBuf, 0, inSize); for (i = 0; i < loopCount; i++) { numRetry = 2; do { ret = sasdiag_sendScsiCdb(ioc, devHandle, &CDB[0], CDBLength, inSize, inBuf, outSize, outBuf); } while ((ret == -EAGAIN) && (--numRetry > 0)); } // for if (ret) { DBG_PRINT(DBG_ERR, "sendBlockCmd: IOC %d cmd 0x%02x FAILED err %d (loop %d)\n", ioc_num, cmdCode, ret, i); return ret; } else { DBG_PRINT(DBG_IO, "sendBlockCmd: IOC %d cmd 0x%02x Successful all %d loops\n", ioc_num, cmdCode, loopCount); } return 0; } int sasdiag_writeReadCompare(U8 ioc_num, U16 devHandle, U16 ioSizeInBlocks, U8 pat, U8 randomAccess, U32 loopCount, bool stopOnErr) { int ret = 0, physBlockSize; U32 i, devSize, lba, inSize = 0, outSize = 0; char *inBuf = NULL, *outBuf = NULL; U8 devCapacity[8]; // For now limit to 64blocks (64 * 512 = 32KB) IO size if (ioSizeInBlocks > 64) ioSizeInBlocks = 64; // To do: enhance to support 520-byte block for EEDP/T10PI // If Inquiry[5] - bit 0 set readcap byte4-7 indicates 512-byte // check if readCap16 byte 12 - bit 0 set ==> EEDP type 1 // Else // check if indicates 520-byte 12 ==> EEDP type 2 physBlockSize = 512; // Get capacity of the device ret = sasdiag_sendScsiCmd(ioc_num, SCSI_COMMAND_READ_CAPACITY, devHandle, 0, 1, &devCapacity[0]); if (ret) { DBG_PRINT(DBG_TEST, "WrRdCompare: IOC %d devHandle 0x%04x failed get device " "capacity, default to 1MB\n", ioc_num, devHandle); devSize = (1024 * 1024) / physBlockSize; } else { devSize = (devCapacity[0] << 24) + (devCapacity[1] << 16) + (devCapacity[2] << 8) + (devCapacity[3] & 0xFFFFFFFF); } inSize = outSize = ioSizeInBlocks * physBlockSize; outBuf = (char *)kmalloc(outSize, GFP_KERNEL); inBuf = (char *)kmalloc(inSize, GFP_KERNEL); DBG_PRINT(DBG_TESTDETAIL, "WrRdCompare: IOC %d devHandle 0x%04x ioSizeInBocks %d " "pat %d loopCount %d inBuf %p outBuf %p\n", ioc_num, devHandle, ioSizeInBlocks, pat, loopCount, inBuf, outBuf); DBG_PRINT(DBG_TEST, "WrRdCompareTest: IOC [%d] devHandle 0x%04x Start ...\n", ioc_num, devHandle); /* W/R whole ramdisk */ for ( lba= 0; lba < devSize; lba += ioSizeInBlocks) { // write out - funct will memset the buf according to data pattern ret = sasdiag_sendBlockCmd(ioc_num, SCSI_COMMAND_WRITE_10, devHandle, ioSizeInBlocks, lba, NULL, 0, outBuf, outSize, pat, 1); if (ret) { DBG_PRINT(DBG_IO, "WrRdCompare: IOC %d FAILED WR at %d loops\n", ioc_num, loopCount); } else { // read back - funct will memset the buf ret = sasdiag_sendBlockCmd(ioc_num, SCSI_COMMAND_READ_10, devHandle, ioSizeInBlocks, lba, inBuf, inSize, NULL, 0, 0, 1); } if (ret) { DBG_PRINT(DBG_IO, "WrRdCompare: IOC %d FAILED RD at loop %d\n", ioc_num, loopCount); } else { // compare if ((ret = memcmp(inBuf, outBuf, inSize)) != 0) { DBG_PRINT(DBG_IO, "WrRdCompare: IOC %d FAILED COMPARE at loop %d\n", ioc_num, loopCount); } } if (stopOnErr && ret) { DBG_PRINT(DBG_TEST, "WrRdCompareTest: IOC [%d] stops on ERROR at loop %d\n", ioc_num, loopCount); break; } } if (ret == 0) DBG_PRINT(DBG_TEST, "WrRdCompareTest: IOC [%d] test end. SUCCESSFUL (%d times)\n\n", ioc_num, loopCount); // free buf mem if(inBuf) kfree(inBuf); if(outBuf) kfree(outBuf); return ret; }