| #include "mv_include.h" |
| |
| #ifdef SUPPORT_CONSOLIDATE |
| |
| #include "consolid.h" |
| |
| #ifdef MV_DEBUG |
| #define TUNE_CONSOLIDATE |
| #endif |
| |
| #ifdef TUNE_CONSOLIDATE |
| #define CONSOLIDATE_STATISTICS_COUNT 8 |
| static MV_U32 gConsolidateStatistics[CONSOLIDATE_STATISTICS_COUNT]; |
| typedef enum{ |
| CONSOLIDATE_NOT_READ_WRITE, |
| CONSOLIDATE_REQUEST_TOO_BIG, |
| CONSOLIDATE_READ_WRITE_DIFFERENT, |
| CONSOLIDATE_NO_RUNNING_REQUEST, |
| CONSOLIDATE_LESS_THAN_SEQUENTIAL_THRESHOLD, |
| CONSOLIDATE_NO_RESOURCE, |
| CONSOLIDATE_GOT_PUSHED, |
| CONSOLIDATA_RESERVED0 |
| }Consolidate_Statistics_Enum; |
| |
| void UpdateConsolidateStatistics(Consolidate_Statistics_Enum catogory) |
| { |
| MV_U8 i; |
| if ( gConsolidateStatistics[catogory]==0xFFFFFFFF ) |
| { |
| for ( i=0; i<CONSOLIDATE_STATISTICS_COUNT; i++ ) |
| MV_DPRINT(("Consolidate statistics[%d]=0x%x.\n", i, |
| gConsolidateStatistics[i])); |
| MV_ZeroMemory(gConsolidateStatistics, sizeof(MV_U32)*CONSOLIDATE_STATISTICS_COUNT); |
| } |
| |
| gConsolidateStatistics[catogory]++; |
| } |
| #else |
| #define UpdateConsolidateStatistics(x) |
| #endif |
| |
| /* |
| * Instruction: How to plug-in this command consolidate sub module to your own module. |
| * 1. Include one .h file which supplies some helper funtions like CONS_GET_EXTENSION |
| * 2. Allocate memory resouce for Consolidate_Extension and Consolidate_Device |
| * 3. Initialize command consolidate module. |
| * Call Consolid_InitializeExtension to initialize Consolidate_Extension |
| * Call Consolid_InitializeDevice for each Consolidate_Device |
| * 4. When you request comes call Consolid_ModuleSendRequest |
| * 5. At proper time, please push command consolidate module. |
| * Sometimes command consolidate is accumulating requests and hasn't sent this internal request, |
| * if there is nothing running now, just push this internal request out. |
| */ |
| #include "core_cons.h" |
| |
| PMV_Request Consolid_GetInternalRequest(MV_PVOID This); |
| void Consolid_InitialInternalRequest(MV_PVOID This, PMV_Request, MV_BOOLEAN); |
| |
| void Consolid_ConsolidateRequest(PConsolidate_Extension, PMV_Request, PMV_Request); |
| void Consolid_CloseRequest(PConsolidate_Extension, PConsolidate_Device, PMV_Request); |
| |
| void Consolid_RequestCallBack(MV_PVOID This, PMV_Request pReq); |
| |
| #ifdef _OS_LINUX |
| #include "hba_timer.h" |
| void CONS_SEND_REQUEST(MV_PVOID this, PMV_Request req) |
| { |
| __hba_dump_req_info(10, req); |
| Core_InternalSendRequest(this, req); |
| } |
| |
| static MV_VOID __consolid_timer_handler(MV_PVOID data) |
| { |
| PMV_Request req = (PMV_Request)data; |
| MV_U8 dev_id = req->Device_Id; |
| PConsolidate_Device cons_dev = |
| CONS_GET_DEVICE(req->Cmd_Initiator, dev_id); |
| MV_U32 sector = req->Data_Transfer_Length>>9; |
| |
| SCSI_CDB10_SET_SECTOR(req->Cdb, sector); |
| cons_dev->Holding_Request = NULL; |
| CONS_SEND_REQUEST(req->Cmd_Initiator, req); |
| } |
| |
| static MV_VOID __consolid_mod_timer(MV_PVOID data, PMV_Request req) |
| { |
| req->Cmd_Initiator = data; |
| if (req->cons_timeout == NULL) { |
| __consolid_timer_handler(req); |
| } |
| __hba_mod_timer(req->cons_timeout, 10, req, __consolid_timer_handler); |
| } |
| |
| static MV_VOID __consolid_del_timer(PMV_Request req) |
| { |
| if (req->cons_timeout) { |
| __hba_del_timer(req->cons_timeout); |
| req->cons_timeout = NULL; |
| } |
| } |
| #endif |
| |
| /* |
| * Consolidate sub-module has got a request. |
| * Two parameters: |
| * This: is the pointer of the command initiator extention pointer. |
| * pReq: request |
| * Will send: |
| * a. one internal request |
| * b. this external request and maybe one holding internal request if exists. |
| * c. NULL if consolidate module holds this request. |
| */ |
| void Consolid_ModuleSendRequest(MV_PVOID This, PMV_Request pReq) |
| { |
| PConsolidate_Extension pCons = CONS_GET_EXTENSION(This); |
| MV_U16 deviceId = pReq->Device_Id; |
| PConsolidate_Device pConsDevice = NULL; |
| PMV_Request pInternal = NULL; |
| MV_LBA startLBA; |
| MV_U32 sectorCount; |
| |
| if ( deviceId>=MAX_DEVICE_NUMBER ) |
| { |
| goto return_original_req; |
| } |
| pConsDevice = CONS_GET_DEVICE(This, deviceId); |
| |
| if( pReq->Req_Flag & REQ_FLAG_NO_CONSOLIDATE ) |
| { |
| goto return_original_req; |
| } |
| |
| /* |
| * We only handle CDB 10 read/write. |
| * Otherwise, change the following code which gets the LBA and Sector Count from the CDB. |
| */ |
| if ( (pReq->Cdb[0]!=SCSI_CMD_READ_10)&&(pReq->Cdb[0]!=SCSI_CMD_WRITE_10) ) |
| { |
| UpdateConsolidateStatistics(CONSOLIDATE_NOT_READ_WRITE); |
| goto return_original_req; |
| } |
| |
| /* It's read/write request. But is it too big for command consolidate */ |
| if ( pReq->Data_Transfer_Length>CONS_MAX_EXTERNAL_REQUEST_SIZE ) |
| { |
| UpdateConsolidateStatistics(CONSOLIDATE_REQUEST_TOO_BIG); |
| goto return_original_req; |
| } |
| |
| /* Check whether they are all read requests or write requests. */ |
| if ( |
| ( (pReq->Cdb[0]==SCSI_CMD_READ_10)&&(!pConsDevice->Is_Read) ) |
| || |
| ( (pReq->Cdb[0]==SCSI_CMD_WRITE_10)&&(pConsDevice->Is_Read) ) |
| ) |
| { |
| UpdateConsolidateStatistics(CONSOLIDATE_READ_WRITE_DIFFERENT); |
| pConsDevice->Is_Read = (pReq->Cdb[0]==SCSI_CMD_READ_10)?1:0; |
| goto return_original_req; |
| } |
| |
| /* Update the consolidate device statistic including last LBA and sequential counter. */ |
| U64_SET_VALUE(startLBA, SCSI_CDB10_GET_LBA(pReq->Cdb)); |
| sectorCount = SCSI_CDB10_GET_SECTOR(pReq->Cdb); |
| /* Check whether it's a sequential request. */ |
| if ( U64_COMPARE_U64(startLBA, pConsDevice->Last_LBA) ) |
| pConsDevice->Sequential = 0; |
| else |
| pConsDevice->Sequential++; /* When equals, return 0. */ |
| |
| /* Last_LBA is actually the next expect sequential LBA. */ |
| pConsDevice->Last_LBA = U64_ADD_U32(startLBA, sectorCount); |
| if ( pConsDevice->Sequential>CONS_SEQUENTIAL_MAX ) /* To avoid overflow */ |
| pConsDevice->Sequential=CONS_SEQUENTIAL_THRESHOLD; |
| |
| /* Is there any requests running on this device? If no, by pass. */ |
| if ( !CONS_DEVICE_IS_BUSY(This, deviceId) ) |
| { |
| UpdateConsolidateStatistics(CONSOLIDATE_NO_RUNNING_REQUEST); |
| goto return_original_req; |
| } |
| #ifdef _OS_WINDOWS |
| /* Do we reach the sequential counter threshold? */ |
| if ( pConsDevice->Sequential<CONS_SEQUENTIAL_THRESHOLD ) |
| { |
| UpdateConsolidateStatistics(CONSOLIDATE_LESS_THAN_SEQUENTIAL_THRESHOLD); |
| goto return_original_req; |
| } |
| #endif |
| pInternal = pConsDevice->Holding_Request; |
| |
| /* Don't accumulate this request too big. */ |
| if ( pInternal && |
| ( (pInternal->Data_Transfer_Length+pReq->Data_Transfer_Length>CONS_MAX_INTERNAL_REQUEST_SIZE) |
| || |
| (pInternal->SG_Table.Valid_Entry_Count+pReq->SG_Table.Valid_Entry_Count>pInternal->SG_Table.Max_Entry_Count) |
| ) |
| ) |
| { |
| Consolid_CloseRequest(pCons, pConsDevice, pInternal); |
| CONS_SEND_REQUEST(This, pInternal); |
| pInternal = NULL; /* After Consolid_CloseRequest, pConsDevice->Holding_Request==NULL */ |
| } |
| |
| /* Get one internal request if we don't have. */ |
| if ( pConsDevice->Holding_Request==NULL ) |
| { |
| pConsDevice->Holding_Request = Consolid_GetInternalRequest(This); |
| } |
| pInternal = pConsDevice->Holding_Request; |
| |
| /* We are out of resource. */ |
| if ( pInternal==NULL ) |
| { |
| UpdateConsolidateStatistics(CONSOLIDATE_NO_RESOURCE); |
| goto return_original_req; |
| } |
| |
| /* Now we should be able to do consolidate requests now. */ |
| Consolid_ConsolidateRequest(pCons, pInternal, pReq); |
| |
| /* Is this internal request bigger enough to send? */ |
| if ( pInternal->Data_Transfer_Length>=CONS_MIN_INTERNAL_REQUEST_SIZE ) |
| { |
| Consolid_CloseRequest(pCons, pConsDevice, pInternal); |
| CONS_SEND_REQUEST(This, pInternal); |
| return; /* Send this internal request. */ |
| } |
| else |
| { |
| #ifdef _OS_LINUX |
| __consolid_mod_timer(This, pInternal); |
| #endif |
| return; /* Hold this request. */ |
| } |
| |
| return_original_req: |
| /* |
| * To keep the command order, |
| * if we cannot do the consolidate for pReq but we hold some internal request, |
| * run the internal request and then run the new pReq. |
| */ |
| if ( pConsDevice && (pConsDevice->Holding_Request) ) |
| { |
| pInternal = pConsDevice->Holding_Request; |
| Consolid_CloseRequest(pCons, pConsDevice, pInternal); |
| /* After Consolid_CloseRequest, pConsDevice->Holding_Request is NULL. */ |
| CONS_SEND_REQUEST(This, pInternal); |
| } |
| CONS_SEND_REQUEST(This, pReq); |
| return; |
| } |
| |
| PMV_Request Consolid_GetInternalRequest(MV_PVOID This) |
| { |
| PConsolidate_Extension pCons = CONS_GET_EXTENSION(This); |
| PMV_Request pReq = NULL; |
| if ( !List_Empty(&pCons->Free_Queue) ) |
| pReq = List_GetFirstEntry(&pCons->Free_Queue, MV_Request, Queue_Pointer); |
| |
| /* Let's intialize this request */ |
| if ( pReq ) |
| Consolid_InitialInternalRequest(This, pReq, MV_FALSE); |
| |
| return pReq; |
| } |
| |
| void Consolid_ReleaseInternalRequest(PConsolidate_Extension pCons, PMV_Request pReq) |
| { |
| #ifdef _OS_LINUX |
| if (pReq->cons_timeout) { |
| __hba_del_timer(pReq->cons_timeout); |
| kfree(pReq->cons_timeout); |
| pReq->cons_timeout = NULL; |
| } |
| #endif |
| List_AddTail(&pReq->Queue_Pointer, &pCons->Free_Queue); |
| } |
| |
| void Consolid_ConsolidateRequest( |
| IN PConsolidate_Extension pCons, |
| IN OUT PMV_Request pInternal, |
| IN PMV_Request pExternal |
| ) |
| { |
| MV_U8 i; |
| PMV_Request pAttachedReq=NULL; |
| |
| /* So far we only handle SCSI Read 10 and SCSI Write 10 */ |
| MV_DASSERT( (pExternal->Cdb[0]==SCSI_CMD_READ_10) || (pExternal->Cdb[0]==SCSI_CMD_WRITE_10) ); |
| pAttachedReq = (PMV_Request) pInternal->Org_Req; |
| |
| if ( pInternal->Data_Transfer_Length==0 ) |
| { |
| /* One external request is attached to that yet. */ |
| pInternal->Device_Id = pExternal->Device_Id; |
| MV_DASSERT( pAttachedReq==NULL ); |
| |
| pInternal->Org_Req = pExternal; |
| MV_LIST_HEAD_INIT( &pExternal->Queue_Pointer ); |
| |
| pInternal->Cdb[0] = pExternal->Cdb[0]; /* Command type */ |
| pInternal->Cdb[2] = pExternal->Cdb[2]; /* Start LBA */ |
| pInternal->Cdb[3] = pExternal->Cdb[3]; |
| pInternal->Cdb[4] = pExternal->Cdb[4]; |
| pInternal->Cdb[5] = pExternal->Cdb[5]; |
| |
| if ( pExternal->Cdb[0]==SCSI_CMD_READ_10 ) |
| { |
| pInternal->Cmd_Flag = CMD_FLAG_DMA | CMD_FLAG_DATA_IN; |
| } |
| else |
| { |
| pInternal->Cmd_Flag = CMD_FLAG_DMA; |
| } |
| } |
| else |
| { |
| MV_DASSERT( pInternal->Device_Id==pExternal->Device_Id ); |
| MV_DASSERT( pAttachedReq!=NULL ); |
| List_AddTail(&pExternal->Queue_Pointer, &pAttachedReq->Queue_Pointer); |
| } |
| |
| /* Don't set the sector count every time. Just before send, set the count. */ |
| pInternal->Data_Transfer_Length += pExternal->Data_Transfer_Length; |
| |
| #ifdef USE_NEW_SGTABLE |
| sgdt_append_reftbl( |
| &pInternal->SG_Table, |
| &pExternal->SG_Table, |
| 0, |
| pExternal->SG_Table.Byte_Count ); |
| |
| #else |
| for ( i=0; i<pExternal->SG_Table.Valid_Entry_Count; i++ ) |
| { |
| SGTable_Append(&pInternal->SG_Table, |
| pExternal->SG_Table.Entry_Ptr[i].Base_Address, |
| pExternal->SG_Table.Entry_Ptr[i].Base_Address_High, |
| pExternal->SG_Table.Entry_Ptr[i].Size); |
| } |
| #endif |
| } |
| |
| void Consolid_CloseRequest( |
| IN PConsolidate_Extension pCons, |
| IN PConsolidate_Device pConsDevice, |
| IN OUT PMV_Request pInternal |
| ) |
| { |
| /* |
| * This internal request is ready for handling now. |
| * Do whatever we need do before we send this request. |
| */ |
| MV_U32 sectorCount = pInternal->Data_Transfer_Length>>9; |
| MV_DASSERT( pInternal->Data_Transfer_Length%512==0 ); |
| #ifdef _OS_LINUX |
| __consolid_del_timer(pInternal); |
| #endif |
| SCSI_CDB10_SET_SECTOR(pInternal->Cdb, sectorCount); |
| pConsDevice->Holding_Request = NULL; |
| } |
| |
| void Consolid_RequestCallBack(MV_PVOID This, PMV_Request pReq) |
| { |
| PConsolidate_Extension pCons = CONS_GET_EXTENSION(This); |
| PConsolidate_Device pConsDevice = CONS_GET_DEVICE(This, pReq->Device_Id); |
| PMV_Request pExternal; |
| PMV_Request pAttachedReq = (PMV_Request) pReq->Org_Req; |
| |
| if ( pReq->Scsi_Status==REQ_STATUS_SUCCESS ) |
| { |
| /* Extract all the external requests. Update status and return. */ |
| while ( !List_Empty(&pAttachedReq->Queue_Pointer) ) |
| { |
| pExternal = List_GetFirstEntry(&pAttachedReq->Queue_Pointer, MV_Request, Queue_Pointer); |
| pExternal->Scsi_Status = REQ_STATUS_SUCCESS; |
| pExternal->Completion(pExternal->Cmd_Initiator, pExternal); |
| } |
| pAttachedReq->Scsi_Status = REQ_STATUS_SUCCESS; |
| pAttachedReq->Completion(pAttachedReq->Cmd_Initiator, pAttachedReq); |
| } |
| else |
| { |
| /* Make sure we won't do consolidate again for these requests. */ |
| pConsDevice->Sequential = 0; |
| MV_DPRINT(("Request error in consolidate.\n")); |
| |
| /* If consolidate request has error, Re-send these original requests. |
| * They go to the hardware directly. Bypass the consolidate module. */ |
| while ( !List_Empty(&pAttachedReq->Queue_Pointer) ) |
| { |
| pExternal = List_GetFirstEntry(&pAttachedReq->Queue_Pointer, MV_Request, Queue_Pointer); |
| pExternal->Req_Flag |= REQ_FLAG_NO_CONSOLIDATE; |
| pExternal->Scsi_Status = REQ_STATUS_PENDING; |
| CONS_SEND_REQUEST(This, pExternal); |
| } |
| CONS_SEND_REQUEST(This, pAttachedReq); |
| } |
| |
| /* Release this request back to the pool. */ |
| Consolid_ReleaseInternalRequest(pCons, pReq); |
| } |
| |
| /* Initialize the command consolidate internal request. */ |
| void Consolid_InitialInternalRequest( |
| IN MV_PVOID This, |
| IN OUT PMV_Request pInternal, |
| IN MV_BOOLEAN firstTime |
| ) |
| { |
| /* |
| * Link pointer: |
| * When request is free, Queue_Pointer is linked together in the request pool queue. |
| * When request is in use, Org_Req is pointer to the first external request. |
| * This first external request uses Queue_Pointer to link other external requests. |
| * We cannot use internal request's Queue_Pointer to link external requests. |
| * Because after send to core driver, this pointer will be destroyed. |
| */ |
| pInternal->Org_Req = NULL; /* Use Queue_Pointer as the linker */ |
| pInternal->Req_Flag |= REQ_FLAG_CONSOLIDATE; |
| pInternal->Scsi_Status = REQ_STATUS_PENDING; |
| pInternal->Data_Transfer_Length = 0; |
| pInternal->Cmd_Flag = 0; |
| SGTable_Init(&pInternal->SG_Table, 0); |
| #ifdef _OS_LINUX |
| if (firstTime == MV_FALSE) { |
| MV_ASSERT(pInternal->cons_timeout == NULL); |
| pInternal->cons_timeout = kmalloc(sizeof(struct timer_list), |
| GFP_ATOMIC); |
| if (pInternal->cons_timeout == NULL) { |
| MV_ASSERT(0); |
| return; |
| } |
| __hba_init_timer(pInternal->cons_timeout); |
| pInternal->Cmd_Initiator = This; |
| } |
| #else |
| MV_ZeroMemory(pInternal->Context, sizeof(MV_PVOID)*MAX_POSSIBLE_MODULE_NUMBER); |
| #endif |
| /* |
| * Some variables only need initialization once. |
| * It won't change no matter during the life time. |
| */ |
| if ( firstTime ) |
| { |
| pInternal->Device_Id = 0; |
| pInternal->Tag = 0; /* Haven't used. */ |
| pInternal->Cmd_Initiator = This; |
| pInternal->Sense_Info_Buffer_Length = 0; |
| pInternal->Sense_Info_Buffer = NULL; |
| pInternal->Data_Buffer = NULL; /* After consolidate, virtual address is not valid. */ |
| #ifdef _OS_LINUX |
| memset(pInternal->Context, 0, sizeof(MV_PVOID)*MAX_POSSIBLE_MODULE_NUMBER); |
| #endif |
| #ifdef _OS_WINDOWS |
| pInternal->Context[MODULE_CORE] = NULL; |
| #endif |
| pInternal->Completion = Consolid_RequestCallBack; |
| MV_LIST_HEAD_INIT(&pInternal->Queue_Pointer); |
| MV_ZeroMemory(pInternal->Cdb, MAX_CDB_SIZE); |
| U64_SET_VALUE(pInternal->LBA, 0); |
| pInternal->Sector_Count = 0; |
| #ifdef _OS_LINUX |
| pInternal->cons_timeout = NULL; |
| #endif |
| } |
| } |
| |
| /* Initialize the Consolidate_Extension */ |
| void Consolid_InitializeExtension(MV_PVOID This) |
| { |
| PConsolidate_Extension pCons = CONS_GET_EXTENSION(This); |
| PMV_Request pReq; |
| MV_U32 i; |
| |
| MV_LIST_HEAD_INIT(&pCons->Free_Queue); |
| for (i = 0; i < CONS_MAX_INTERNAL_REQUEST_COUNT; i++) { |
| pReq = &pCons->Requests[i]; |
| |
| Consolid_InitialInternalRequest(This, pReq, MV_TRUE); |
| List_AddTail(&pReq->Queue_Pointer, &pCons->Free_Queue); |
| } |
| } |
| |
| /* |
| * Initialize the Consolidate_Device. |
| * I don't initialize all the devices at once. |
| * Caller should call device one by one. |
| * So in this way, consolidate module doesn't all the Consolidate_Device are together |
| * or they are embedded in some caller data structure. |
| * One more advantage is that caller itself can map the Device_Id to related Consolidate_Device buffer. |
| * We don't need contiguous Device_Id. |
| */ |
| void Consolid_InitializeDevice(MV_PVOID This, MV_U16 Device_Id) |
| { |
| PConsolidate_Device pConsDevice = CONS_GET_DEVICE(This, Device_Id); |
| |
| MV_ZeroMemory(pConsDevice, sizeof(Consolidate_Device)); |
| } |
| |
| |
| /* |
| * Caller pushes us to send the holding request if any. |
| */ |
| void |
| Consolid_PushSendRequest( |
| MV_PVOID This, |
| MV_U16 Device_Id |
| ) |
| { |
| PConsolidate_Extension pCons = CONS_GET_EXTENSION(This); |
| PConsolidate_Device pConsDevice = CONS_GET_DEVICE(This, Device_Id); |
| PMV_Request pInternal = pConsDevice->Holding_Request; |
| |
| if ( pInternal==NULL ) return; |
| |
| UpdateConsolidateStatistics(CONSOLIDATE_GOT_PUSHED); |
| |
| Consolid_CloseRequest(pCons, pConsDevice, pInternal); |
| /* After Consolid_CloseRequest pConsDevice->Holding_Request is NULL. */ |
| CONS_SEND_REQUEST(This, pInternal); |
| } |
| #endif |
| |