3 Copyright (c) 2008-2010 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_LDEV(Srb
) & 1];
116 PrintNtConsole("q: chan = %#x, dev %#x\n", chan, GET_LDEV(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_LDEV(Srb
) & 1;
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
->lun
[0]->queue_depth
* (chan
->lun
[0]->LunSelectWaitCount
+1) >
310 chan
->lun
[1]->queue_depth
* (chan
->lun
[1]->LunSelectWaitCount
+1)) {
315 /* // prevent too long wait for actively used device
316 if(chan->lun[cdev ^ 1]->queue_depth &&
317 chan->lun[cdev ^ 1]->LunSelectWaitCount >= chan->lun[cdev]->queue_depth) {
320 // get next request for processing
321 chan
->cur_req
= chan
->lun
[cdev
]->first_req
;
322 chan
->cur_cdev
= cdev
;
323 if(!chan
->lun
[cdev
^ 1]->queue_depth
) {
324 chan
->lun
[cdev
^ 1]->LunSelectWaitCount
=0;
326 chan
->lun
[cdev
^ 1]->LunSelectWaitCount
++;
328 chan
->lun
[cdev
]->LunSelectWaitCount
=0;
330 // chan->first_req->prev_req = NULL;
331 AtaReq
->ReqState
= REQ_STATE_NONE
;
333 ASSERT(chan->queue_depth ==
334 (chan->lun[0]->queue_depth + chan->lun[1]->queue_depth));
335 ASSERT(!chan->queue_depth || chan->cur_req);
338 } // end UniataRemoveRequest()
341 Get currently processed request
342 (from head of the queue)
350 // if(!chan->lun[]->queue_depth) {
351 if(!chan
|| !chan
->cur_req
) {
355 return chan
->cur_req
->Srb
;
356 } // end UniataGetCurRequest()
359 Get next channel to be serviced
360 (used in simplex mode only)
364 UniataGetNextChannel(
369 PHW_DEVICE_EXTENSION deviceExtension
;
373 deviceExtension
= chan
->DeviceExtension
;
375 if(!deviceExtension
->simplexOnly
) {
378 KdPrint2((PRINT_PREFIX
"UniataGetNextChannel:\n"));
381 for(_c
=0; _c
<deviceExtension
->NumberChannels
; _c
++) {
382 c
= (_c
+deviceExtension
->FirstChannelToCheck
) % deviceExtension
->NumberChannels
;
383 chan
= &deviceExtension
->chan
[c
];
384 if(chan
->queue_depth
&&
385 chan
->queue_depth
* (chan
->ChannelSelectWaitCount
+1) >
388 cost_c
= chan
->queue_depth
* (chan
->ChannelSelectWaitCount
+1);
391 if(best_c
== CHAN_NOT_SPECIFIED
) {
392 KdPrint2((PRINT_PREFIX
" empty queues\n"));
395 deviceExtension
->FirstChannelToCheck
= c
;
396 for(_c
=0; _c
<deviceExtension
->NumberChannels
; _c
++) {
397 chan
= &deviceExtension
->chan
[_c
];
399 chan
->ChannelSelectWaitCount
= 0;
402 chan
->ChannelSelectWaitCount
++;
403 if(!chan
->queue_depth
) {
404 chan
->ChannelSelectWaitCount
= 0;
406 chan
->ChannelSelectWaitCount
++;
409 KdPrint2((PRINT_PREFIX
" select chan %d\n", best_c
));
410 return &deviceExtension
->chan
[best_c
];
411 } // end UniataGetNextChannel()