3 Copyright (c) 2008-2012 Alexandr A. Telyatnikov (Alter)
9 This module handles comamnd queue reordering and channel load balance
12 Alexander A. Telyatnikov (Alter)
19 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37 Get cost of insertion Req1 before Req2
42 PHW_LU_EXTENSION LunExt
,
52 // can't insert Req1 before tooooo old Req2
54 return REORDER_COST_TTL
;
55 // check if reorderable
56 flags1
= AtaReq1
->Flags
;
57 flags2
= AtaReq2
->Flags
;
58 if(!((flags2
& flags1
) & REQ_FLAG_REORDERABLE_CMD
))
59 return REORDER_COST_DENIED
;
60 // if at least one Req is WRITE, target areas
62 write1
= (flags1
& REQ_FLAG_RW_MASK
) == REQ_FLAG_WRITE
;
63 write2
= (flags2
& REQ_FLAG_RW_MASK
) == REQ_FLAG_WRITE
;
64 cost
= AtaReq1
->lba
+AtaReq1
->bcount
- AtaReq2
->lba
;
65 if( write1
|| write2
) {
66 // check for intersection
67 if((AtaReq1
->lba
< AtaReq2
->lba
+AtaReq2
->bcount
) &&
68 (AtaReq1
->lba
+AtaReq1
->bcount
> AtaReq2
->lba
)) {
70 return REORDER_COST_INTERSECT
;
74 cost
*= (1-LunExt
->SeekBackMCost
);
76 cost
*= (LunExt
->SeekBackMCost
-1);
78 if( write1
== write2
) {
81 return (cost
* LunExt
->RwSwitchMCost
) + LunExt
->RwSwitchCost
;
82 } // end UniataGetCost()
85 Insert command to proper place of command queue
86 Perform reorder if necessary
92 IN PSCSI_REQUEST_BLOCK Srb
95 PATA_REQ AtaReq
= (PATA_REQ
)(Srb
->SrbExtension
);
103 PATA_REQ BestAtaReq1
;
105 BOOLEAN use_reorder
= chan
->UseReorder
/*EnableReorder*/;
106 #ifdef QUEUE_STATISTICS
107 BOOLEAN reordered
= FALSE
;
108 #endif //QUEUE_STATISTICS
110 PHW_LU_EXTENSION LunExt
= chan
->lun
[GET_CDEV(Srb
)];
116 PrintNtConsole("q: chan = %#x, dev %#x\n", chan, GET_CDEV(Srb));
118 for(i=0; i<1000; i++) {
119 AtapiStallExecution(5*1000);
125 // check if queue is empty
126 if(LunExt
->queue_depth
) {
127 AtaReq
->ttl
= (UCHAR
)(max(LunExt
->queue_depth
, MIN_REQ_TTL
));
131 AtaReq2
= LunExt
->last_req
;
134 (Srb
->SrbFlags
& SRB_FLAGS_QUEUE_ACTION_ENABLE
)) {
135 switch(Srb
->QueueAction
) {
136 case SRB_ORDERED_QUEUE_TAG_REQUEST
:
138 #ifdef QUEUE_STATISTICS
139 chan
->TryReorderTailCount
++;
140 #endif //QUEUE_STATISTICS
142 case SRB_HEAD_OF_QUEUE_TAG_REQUEST
:
143 BestAtaReq1
= LunExt
->first_req
;
144 best_cost
= -REORDER_COST_RESELECT
;
145 #ifdef QUEUE_STATISTICS
146 chan
->TryReorderHeadCount
++;
147 #endif //QUEUE_STATISTICS
149 case SRB_SIMPLE_TAG_REQUEST
:
151 best_cost
= UniataGetCost(LunExt
, BestAtaReq1
, AtaReq
);
156 best_cost
= UniataGetCost(LunExt
, BestAtaReq1
, AtaReq
);
161 #ifdef QUEUE_STATISTICS
162 chan
->TryReorderCount
++;
163 #endif //QUEUE_STATISTICS
165 // walk through command queue and find the best
166 // place for insertion of the command
167 while ((AtaReq1
= AtaReq2
->prev_req
)) {
168 new_cost1
= UniataGetCost(LunExt
, AtaReq1
, AtaReq
);
169 new_cost2
= UniataGetCost(LunExt
, AtaReq
, AtaReq2
);
171 #ifdef QUEUE_STATISTICS
172 if(new_cost1
== REORDER_COST_INTERSECT
||
173 new_cost2
== REORDER_COST_INTERSECT
)
174 chan
->IntersectCount
++;
175 #endif //QUEUE_STATISTICS
177 if(new_cost2
> REORDER_COST_RESELECT
)
180 // for now I see nothing bad in RESELECT here
181 //ASSERT(new_cost1 <= REORDER_COST_RESELECT);
183 new_cost
= UniataGetCost(LunExt
, AtaReq1
, AtaReq2
);
184 new_cost
= new_cost1
+ new_cost2
- new_cost
;
186 // check for Stop Reordering
187 if(new_cost
> REORDER_COST_RESELECT
*3)
190 if(new_cost
< best_cost
) {
191 best_cost
= new_cost
;
192 BestAtaReq1
= AtaReq1
;
193 #ifdef QUEUE_STATISTICS
195 #endif //QUEUE_STATISTICS
199 #ifdef QUEUE_STATISTICS
201 chan
->ReorderCount
++;
202 #endif //QUEUE_STATISTICS
204 // Insert Req between Req2 & Req1
205 AtaReq2
= BestAtaReq1
->next_req
;
207 AtaReq2
->prev_req
= AtaReq
;
208 AtaReq
->next_req
= AtaReq2
;
210 AtaReq
->next_req
= NULL
;
211 LunExt
->last_req
= AtaReq
;
213 // LunExt->last_req->next_req = AtaReq;
214 BestAtaReq1
->next_req
= AtaReq
;
215 // AtaReq->prev_req = LunExt->last_req;
216 AtaReq
->prev_req
= BestAtaReq1
;
219 while((AtaReq1
= AtaReq1
->next_req
)) {
220 //ASSERT(AtaReq1->ttl);
228 AtaReq
->next_req
= NULL
;
230 LunExt
->last_req
= AtaReq
;
232 LunExt
->queue_depth
++;
234 chan
->DeviceExtension
->queue_depth
++;
235 // check if this is the 1st request in queue
236 if(chan
->queue_depth
== 1) {
237 chan
->cur_req
= LunExt
->first_req
;
240 #ifdef QUEUE_STATISTICS
241 //ASSERT(LunExt->queue_depth);
242 chan
->QueueStat
[min(MAX_QUEUE_STAT
, LunExt
->queue_depth
)-1]++;
243 #endif //QUEUE_STATISTICS
245 ASSERT(chan->queue_depth ==
246 (chan->lun[0]->queue_depth + chan->lun[1]->queue_depth));
247 ASSERT(!chan->queue_depth || chan->cur_req);
250 } // end UniataQueueRequest()
253 Remove request from queue and get next request
259 IN PSCSI_REQUEST_BLOCK Srb
267 PATA_REQ AtaReq
= (PATA_REQ
)(Srb
->SrbExtension
);
268 //PHW_DEVICE_EXTENSION deviceExtension = chan->DeviceExtension;
270 ULONG cdev
= GET_CDEV(Srb
);
271 PHW_LU_EXTENSION LunExt
= chan
->lun
[cdev
];
277 ASSERT(chan->queue_depth ==
278 (chan->lun[0]->queue_depth + chan->lun[1]->queue_depth));
279 ASSERT(!chan->queue_depth || chan->cur_req);
281 // check if queue for the device is empty
282 if(!LunExt
->queue_depth
)
285 // check if request is under processing (busy)
286 if(!AtaReq
->ReqState
)
289 // remove reqest from queue
290 if(AtaReq
->prev_req
) {
291 AtaReq
->prev_req
->next_req
=
294 LunExt
->first_req
= AtaReq
->next_req
;
296 if(AtaReq
->next_req
) {
297 AtaReq
->next_req
->prev_req
=
300 LunExt
->last_req
= AtaReq
->prev_req
;
302 LunExt
->queue_depth
--;
304 chan
->DeviceExtension
->queue_depth
--;
305 // set LastWrite flag for Lun
306 LunExt
->last_write
= ((AtaReq
->Flags
& REQ_FLAG_RW_MASK
) == REQ_FLAG_WRITE
);
308 // get request from longest queue to balance load
309 if(chan
->NumberLuns
> 1) {
310 if(chan
->lun
[0]->queue_depth
* (chan
->lun
[0]->LunSelectWaitCount
+1) >
311 chan
->lun
[1]->queue_depth
* (chan
->lun
[1]->LunSelectWaitCount
+1)) {
317 /* // prevent too long wait for actively used device
318 if(chan->lun[cdev ^ 1]->queue_depth &&
319 chan->lun[cdev ^ 1]->LunSelectWaitCount >= chan->lun[cdev]->queue_depth) {
322 // get next request for processing
323 chan
->cur_req
= chan
->lun
[cdev
]->first_req
;
324 chan
->cur_cdev
= cdev
;
325 if(chan
->NumberLuns
> 1) {
326 if(!chan
->lun
[cdev
^ 1]->queue_depth
) {
327 chan
->lun
[cdev
^ 1]->LunSelectWaitCount
=0;
329 chan
->lun
[cdev
^ 1]->LunSelectWaitCount
++;
332 chan
->lun
[cdev
]->LunSelectWaitCount
=0;
334 // chan->first_req->prev_req = NULL;
335 AtaReq
->ReqState
= REQ_STATE_NONE
;
337 ASSERT(chan->queue_depth ==
338 (chan->lun[0]->queue_depth + chan->lun[1]->queue_depth));
339 ASSERT(!chan->queue_depth || chan->cur_req);
342 } // end UniataRemoveRequest()
345 Get currently processed request
346 (from head of the queue)
354 // if(!chan->lun[]->queue_depth) {
355 if(!chan
|| !chan
->cur_req
) {
359 return chan
->cur_req
->Srb
;
360 } // end UniataGetCurRequest()
363 Get next channel to be serviced
364 (used in simplex mode only)
368 UniataGetNextChannel(
373 PHW_DEVICE_EXTENSION deviceExtension
;
377 deviceExtension
= chan
->DeviceExtension
;
379 if(!deviceExtension
->simplexOnly
) {
382 KdPrint2((PRINT_PREFIX
"UniataGetNextChannel:\n"));
385 for(_c
=0; _c
<deviceExtension
->NumberChannels
; _c
++) {
386 c
= (_c
+deviceExtension
->FirstChannelToCheck
) % deviceExtension
->NumberChannels
;
387 chan
= &deviceExtension
->chan
[c
];
388 if(chan
->queue_depth
&&
389 chan
->queue_depth
* (chan
->ChannelSelectWaitCount
+1) >
392 cost_c
= chan
->queue_depth
* (chan
->ChannelSelectWaitCount
+1);
395 if(best_c
== CHAN_NOT_SPECIFIED
) {
396 KdPrint2((PRINT_PREFIX
" empty queues\n"));
399 deviceExtension
->FirstChannelToCheck
= c
;
400 for(_c
=0; _c
<deviceExtension
->NumberChannels
; _c
++) {
401 chan
= &deviceExtension
->chan
[_c
];
403 chan
->ChannelSelectWaitCount
= 0;
406 chan
->ChannelSelectWaitCount
++;
407 if(!chan
->queue_depth
) {
408 chan
->ChannelSelectWaitCount
= 0;
410 chan
->ChannelSelectWaitCount
++;
413 KdPrint2((PRINT_PREFIX
" select chan %d\n", best_c
));
414 return &deviceExtension
->chan
[best_c
];
415 } // end UniataGetNextChannel()