1 /* $NetBSD: rf_sstf.c,v 1.16 2009/03/14 15:36:20 dsl Exp $ */ 2 /* 3 * Copyright (c) 1995 Carnegie-Mellon University. 4 * All rights reserved. 5 * 6 * Author: Jim Zelenka 7 * 8 * Permission to use, copy, modify and distribute this software and 9 * its documentation is hereby granted, provided that both the copyright 10 * notice and this permission notice appear in all copies of the 11 * software, derivative works or modified versions, and any portions 12 * thereof, and that both notices appear in supporting documentation. 13 * 14 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" 15 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND 16 * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 17 * 18 * Carnegie Mellon requests users of this software to return to 19 * 20 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 21 * School of Computer Science 22 * Carnegie Mellon University 23 * Pittsburgh PA 15213-3890 24 * 25 * any improvements or extensions that they make and grant Carnegie the 26 * rights to redistribute these changes. 27 */ 28 29 /******************************************************************************* 30 * 31 * sstf.c -- prioritized shortest seek time first disk queueing code 32 * 33 ******************************************************************************/ 34 35 #include <sys/cdefs.h> 36 __KERNEL_RCSID(0, "$NetBSD: rf_sstf.c,v 1.16 2009/03/14 15:36:20 dsl Exp $"); 37 38 #include <dev/raidframe/raidframevar.h> 39 40 #include "rf_alloclist.h" 41 #include "rf_stripelocks.h" 42 #include "rf_layout.h" 43 #include "rf_diskqueue.h" 44 #include "rf_sstf.h" 45 #include "rf_debugMem.h" 46 #include "rf_general.h" 47 #include "rf_options.h" 48 #include "rf_raid.h" 49 50 #define DIR_LEFT 1 51 #define DIR_RIGHT 2 52 #define DIR_EITHER 3 53 54 #define SNUM_DIFF(_a_,_b_) (((_a_)>(_b_))?((_a_)-(_b_)):((_b_)-(_a_))) 55 56 #define QSUM(_sstfq_) (((_sstfq_)->lopri.qlen)+((_sstfq_)->left.qlen)+((_sstfq_)->right.qlen)) 57 58 59 static void 60 do_sstf_ord_q(RF_DiskQueueData_t **, 61 RF_DiskQueueData_t **, 62 RF_DiskQueueData_t *); 63 64 static RF_DiskQueueData_t * 65 closest_to_arm(RF_SstfQ_t *, 66 RF_SectorNum_t, 67 int *, 68 int); 69 static void do_dequeue(RF_SstfQ_t *, RF_DiskQueueData_t *); 70 71 72 static void 73 do_sstf_ord_q(RF_DiskQueueData_t **queuep, RF_DiskQueueData_t **tailp, RF_DiskQueueData_t *req) 74 { 75 RF_DiskQueueData_t *r, *s; 76 77 if (*queuep == NULL) { 78 *queuep = req; 79 *tailp = req; 80 req->next = NULL; 81 req->prev = NULL; 82 return; 83 } 84 if (req->sectorOffset <= (*queuep)->sectorOffset) { 85 req->next = *queuep; 86 req->prev = NULL; 87 (*queuep)->prev = req; 88 *queuep = req; 89 return; 90 } 91 if (req->sectorOffset > (*tailp)->sectorOffset) { 92 /* optimization */ 93 r = NULL; 94 s = *tailp; 95 goto q_at_end; 96 } 97 for (s = NULL, r = *queuep; r; s = r, r = r->next) { 98 if (r->sectorOffset >= req->sectorOffset) { 99 /* insert after s, before r */ 100 RF_ASSERT(s); 101 req->next = r; 102 r->prev = req; 103 s->next = req; 104 req->prev = s; 105 return; 106 } 107 } 108 q_at_end: 109 /* insert after s, at end of queue */ 110 RF_ASSERT(r == NULL); 111 RF_ASSERT(s); 112 RF_ASSERT(s == (*tailp)); 113 req->next = NULL; 114 req->prev = s; 115 s->next = req; 116 *tailp = req; 117 } 118 /* for removing from head-of-queue */ 119 #define DO_HEAD_DEQ(_r_,_q_) { \ 120 _r_ = (_q_)->queue; \ 121 RF_ASSERT((_r_) != NULL); \ 122 (_q_)->queue = (_r_)->next; \ 123 (_q_)->qlen--; \ 124 if ((_q_)->qlen == 0) { \ 125 RF_ASSERT((_r_) == (_q_)->qtail); \ 126 RF_ASSERT((_q_)->queue == NULL); \ 127 (_q_)->qtail = NULL; \ 128 } \ 129 else { \ 130 RF_ASSERT((_q_)->queue->prev == (_r_)); \ 131 (_q_)->queue->prev = NULL; \ 132 } \ 133 } 134 135 /* for removing from end-of-queue */ 136 #define DO_TAIL_DEQ(_r_,_q_) { \ 137 _r_ = (_q_)->qtail; \ 138 RF_ASSERT((_r_) != NULL); \ 139 (_q_)->qtail = (_r_)->prev; \ 140 (_q_)->qlen--; \ 141 if ((_q_)->qlen == 0) { \ 142 RF_ASSERT((_r_) == (_q_)->queue); \ 143 RF_ASSERT((_q_)->qtail == NULL); \ 144 (_q_)->queue = NULL; \ 145 } \ 146 else { \ 147 RF_ASSERT((_q_)->qtail->next == (_r_)); \ 148 (_q_)->qtail->next = NULL; \ 149 } \ 150 } 151 152 #define DO_BEST_DEQ(_l_,_r_,_q_) { \ 153 if (SNUM_DIFF((_q_)->queue->sectorOffset,_l_) \ 154 < SNUM_DIFF((_q_)->qtail->sectorOffset,_l_)) \ 155 { \ 156 DO_HEAD_DEQ(_r_,_q_); \ 157 } \ 158 else { \ 159 DO_TAIL_DEQ(_r_,_q_); \ 160 } \ 161 } 162 163 static RF_DiskQueueData_t * 164 closest_to_arm(RF_SstfQ_t *queue, RF_SectorNum_t arm_pos, int *dir, int allow_reverse) 165 { 166 RF_SectorNum_t best_pos_l = 0, this_pos_l = 0, last_pos = 0; 167 RF_SectorNum_t best_pos_r = 0, this_pos_r = 0; 168 RF_DiskQueueData_t *r, *best_l, *best_r; 169 170 best_r = best_l = NULL; 171 for (r = queue->queue; r; r = r->next) { 172 if (r->sectorOffset < arm_pos) { 173 if (best_l == NULL) { 174 best_l = r; 175 last_pos = best_pos_l = this_pos_l; 176 } else { 177 this_pos_l = arm_pos - r->sectorOffset; 178 if (this_pos_l < best_pos_l) { 179 best_l = r; 180 last_pos = best_pos_l = this_pos_l; 181 } else { 182 last_pos = this_pos_l; 183 } 184 } 185 } else { 186 if (best_r == NULL) { 187 best_r = r; 188 last_pos = best_pos_r = this_pos_r; 189 } else { 190 this_pos_r = r->sectorOffset - arm_pos; 191 if (this_pos_r < best_pos_r) { 192 best_r = r; 193 last_pos = best_pos_r = this_pos_r; 194 } else { 195 last_pos = this_pos_r; 196 } 197 if (this_pos_r > last_pos) { 198 /* getting farther away */ 199 break; 200 } 201 } 202 } 203 } 204 if ((best_r == NULL) && (best_l == NULL)) 205 return (NULL); 206 if ((*dir == DIR_RIGHT) && best_r) 207 return (best_r); 208 if ((*dir == DIR_LEFT) && best_l) 209 return (best_l); 210 if (*dir == DIR_EITHER) { 211 if (best_l == NULL) 212 return (best_r); 213 if (best_r == NULL) 214 return (best_l); 215 if (best_pos_r < best_pos_l) 216 return (best_r); 217 else 218 return (best_l); 219 } 220 /* 221 * Nothing in the direction we want to go. Reverse or 222 * reset the arm. We know we have an I/O in the other 223 * direction. 224 */ 225 if (allow_reverse) { 226 if (*dir == DIR_RIGHT) { 227 *dir = DIR_LEFT; 228 return (best_l); 229 } else { 230 *dir = DIR_RIGHT; 231 return (best_r); 232 } 233 } 234 /* 235 * Reset (beginning of queue). 236 */ 237 RF_ASSERT(*dir == DIR_RIGHT); 238 return (queue->queue); 239 } 240 241 void * 242 rf_SstfCreate( 243 RF_SectorCount_t sect_per_disk, 244 RF_AllocListElem_t *cl_list, 245 RF_ShutdownList_t **listp) 246 { 247 RF_Sstf_t *sstfq; 248 249 RF_MallocAndAdd(sstfq, sizeof(RF_Sstf_t), (RF_Sstf_t *), cl_list); 250 sstfq->dir = DIR_EITHER; 251 sstfq->allow_reverse = 1; 252 return ((void *) sstfq); 253 } 254 255 void * 256 rf_ScanCreate( 257 RF_SectorCount_t sect_per_disk, 258 RF_AllocListElem_t *cl_list, 259 RF_ShutdownList_t **listp) 260 { 261 RF_Sstf_t *scanq; 262 263 RF_MallocAndAdd(scanq, sizeof(RF_Sstf_t), (RF_Sstf_t *), cl_list); 264 scanq->dir = DIR_RIGHT; 265 scanq->allow_reverse = 1; 266 return ((void *) scanq); 267 } 268 269 void * 270 rf_CscanCreate( 271 RF_SectorCount_t sect_per_disk, 272 RF_AllocListElem_t *cl_list, 273 RF_ShutdownList_t **listp) 274 { 275 RF_Sstf_t *cscanq; 276 277 RF_MallocAndAdd(cscanq, sizeof(RF_Sstf_t), (RF_Sstf_t *), cl_list); 278 cscanq->dir = DIR_RIGHT; 279 return ((void *) cscanq); 280 } 281 282 void 283 rf_SstfEnqueue(void *qptr, RF_DiskQueueData_t *req, int priority) 284 { 285 RF_Sstf_t *sstfq; 286 287 sstfq = (RF_Sstf_t *) qptr; 288 289 if (priority == RF_IO_LOW_PRIORITY) { 290 #if RF_DEBUG_QUEUE 291 if (rf_sstfDebug || rf_scanDebug || rf_cscanDebug) { 292 RF_DiskQueue_t *dq; 293 dq = (RF_DiskQueue_t *) req->queue; 294 printf("raid%d: ENQ lopri %d queues are %d,%d,%d\n", 295 req->raidPtr->raidid, 296 dq->col, 297 sstfq->left.qlen, sstfq->right.qlen, 298 sstfq->lopri.qlen); 299 } 300 #endif 301 do_sstf_ord_q(&sstfq->lopri.queue, &sstfq->lopri.qtail, req); 302 sstfq->lopri.qlen++; 303 } else { 304 if (req->sectorOffset < sstfq->last_sector) { 305 do_sstf_ord_q(&sstfq->left.queue, &sstfq->left.qtail, req); 306 sstfq->left.qlen++; 307 } else { 308 do_sstf_ord_q(&sstfq->right.queue, &sstfq->right.qtail, req); 309 sstfq->right.qlen++; 310 } 311 } 312 } 313 314 static void 315 do_dequeue(RF_SstfQ_t *queue, RF_DiskQueueData_t *req) 316 { 317 RF_DiskQueueData_t *req2; 318 319 #if RF_DEBUG_QUEUE 320 if (rf_sstfDebug || rf_scanDebug || rf_cscanDebug) { 321 printf("raid%d: do_dequeue\n", req->raidPtr->raidid); 322 } 323 #endif 324 if (req == queue->queue) { 325 DO_HEAD_DEQ(req2, queue); 326 RF_ASSERT(req2 == req); 327 } else 328 if (req == queue->qtail) { 329 DO_TAIL_DEQ(req2, queue); 330 RF_ASSERT(req2 == req); 331 } else { 332 /* dequeue from middle of list */ 333 RF_ASSERT(req->next); 334 RF_ASSERT(req->prev); 335 queue->qlen--; 336 req->next->prev = req->prev; 337 req->prev->next = req->next; 338 req->next = req->prev = NULL; 339 } 340 } 341 342 RF_DiskQueueData_t * 343 rf_SstfDequeue(void *qptr) 344 { 345 RF_DiskQueueData_t *req = NULL; 346 RF_Sstf_t *sstfq; 347 348 sstfq = (RF_Sstf_t *) qptr; 349 350 #if RF_DEBUG_QUEUE 351 if (rf_sstfDebug) { 352 RF_DiskQueue_t *dq; 353 dq = (RF_DiskQueue_t *) req->queue; 354 RF_ASSERT(QSUM(sstfq) == dq->queueLength); 355 printf("raid%d: sstf: Dequeue %d queues are %d,%d,%d\n", 356 req->raidPtr->raidid, dq->col, 357 sstfq->left.qlen, sstfq->right.qlen, sstfq->lopri.qlen); 358 } 359 #endif 360 if (sstfq->left.queue == NULL) { 361 RF_ASSERT(sstfq->left.qlen == 0); 362 if (sstfq->right.queue == NULL) { 363 RF_ASSERT(sstfq->right.qlen == 0); 364 if (sstfq->lopri.queue == NULL) { 365 RF_ASSERT(sstfq->lopri.qlen == 0); 366 return (NULL); 367 } 368 #if RF_DEBUG_QUEUE 369 if (rf_sstfDebug) { 370 printf("raid%d: sstf: check for close lopri", 371 req->raidPtr->raidid); 372 } 373 #endif 374 req = closest_to_arm(&sstfq->lopri, sstfq->last_sector, 375 &sstfq->dir, sstfq->allow_reverse); 376 #if RF_DEBUG_QUEUE 377 if (rf_sstfDebug) { 378 printf("raid%d: sstf: closest_to_arm said %lx", 379 req->raidPtr->raidid, (long) req); 380 } 381 #endif 382 if (req == NULL) 383 return (NULL); 384 do_dequeue(&sstfq->lopri, req); 385 } else { 386 DO_BEST_DEQ(sstfq->last_sector, req, &sstfq->right); 387 } 388 } else { 389 if (sstfq->right.queue == NULL) { 390 RF_ASSERT(sstfq->right.qlen == 0); 391 DO_BEST_DEQ(sstfq->last_sector, req, &sstfq->left); 392 } else { 393 if (SNUM_DIFF(sstfq->last_sector, sstfq->right.queue->sectorOffset) 394 < SNUM_DIFF(sstfq->last_sector, sstfq->left.qtail->sectorOffset)) { 395 DO_HEAD_DEQ(req, &sstfq->right); 396 } else { 397 DO_TAIL_DEQ(req, &sstfq->left); 398 } 399 } 400 } 401 RF_ASSERT(req); 402 sstfq->last_sector = req->sectorOffset; 403 return (req); 404 } 405 406 RF_DiskQueueData_t * 407 rf_ScanDequeue(void *qptr) 408 { 409 RF_DiskQueueData_t *req = NULL; 410 RF_Sstf_t *scanq; 411 412 scanq = (RF_Sstf_t *) qptr; 413 414 #if RF_DEBUG_QUEUE 415 if (rf_scanDebug) { 416 RF_DiskQueue_t *dq; 417 dq = (RF_DiskQueue_t *) req->queue; 418 RF_ASSERT(QSUM(scanq) == dq->queueLength); 419 printf("raid%d: scan: Dequeue %d queues are %d,%d,%d\n", 420 req->raidPtr->raidid, dq->col, 421 scanq->left.qlen, scanq->right.qlen, scanq->lopri.qlen); 422 } 423 #endif 424 if (scanq->left.queue == NULL) { 425 RF_ASSERT(scanq->left.qlen == 0); 426 if (scanq->right.queue == NULL) { 427 RF_ASSERT(scanq->right.qlen == 0); 428 if (scanq->lopri.queue == NULL) { 429 RF_ASSERT(scanq->lopri.qlen == 0); 430 return (NULL); 431 } 432 req = closest_to_arm(&scanq->lopri, scanq->last_sector, 433 &scanq->dir, scanq->allow_reverse); 434 if (req == NULL) 435 return (NULL); 436 do_dequeue(&scanq->lopri, req); 437 } else { 438 scanq->dir = DIR_RIGHT; 439 DO_HEAD_DEQ(req, &scanq->right); 440 } 441 } else 442 if (scanq->right.queue == NULL) { 443 RF_ASSERT(scanq->right.qlen == 0); 444 RF_ASSERT(scanq->left.queue); 445 scanq->dir = DIR_LEFT; 446 DO_TAIL_DEQ(req, &scanq->left); 447 } else { 448 RF_ASSERT(scanq->right.queue); 449 RF_ASSERT(scanq->left.queue); 450 if (scanq->dir == DIR_RIGHT) { 451 DO_HEAD_DEQ(req, &scanq->right); 452 } else { 453 DO_TAIL_DEQ(req, &scanq->left); 454 } 455 } 456 RF_ASSERT(req); 457 scanq->last_sector = req->sectorOffset; 458 return (req); 459 } 460 461 RF_DiskQueueData_t * 462 rf_CscanDequeue(void *qptr) 463 { 464 RF_DiskQueueData_t *req = NULL; 465 RF_Sstf_t *cscanq; 466 467 cscanq = (RF_Sstf_t *) qptr; 468 469 RF_ASSERT(cscanq->dir == DIR_RIGHT); 470 #if RF_DEBUG_QUEUE 471 if (rf_cscanDebug) { 472 RF_DiskQueue_t *dq; 473 dq = (RF_DiskQueue_t *) req->queue; 474 RF_ASSERT(QSUM(cscanq) == dq->queueLength); 475 printf("raid%d: scan: Dequeue %d queues are %d,%d,%d\n", 476 req->raidPtr->raidid, dq->col, 477 cscanq->left.qlen, cscanq->right.qlen, 478 cscanq->lopri.qlen); 479 } 480 #endif 481 if (cscanq->right.queue) { 482 DO_HEAD_DEQ(req, &cscanq->right); 483 } else { 484 RF_ASSERT(cscanq->right.qlen == 0); 485 if (cscanq->left.queue == NULL) { 486 RF_ASSERT(cscanq->left.qlen == 0); 487 if (cscanq->lopri.queue == NULL) { 488 RF_ASSERT(cscanq->lopri.qlen == 0); 489 return (NULL); 490 } 491 req = closest_to_arm(&cscanq->lopri, cscanq->last_sector, 492 &cscanq->dir, cscanq->allow_reverse); 493 if (req == NULL) 494 return (NULL); 495 do_dequeue(&cscanq->lopri, req); 496 } else { 497 /* 498 * There's I/Os to the left of the arm. Swing 499 * on back (swap queues). 500 */ 501 cscanq->right = cscanq->left; 502 cscanq->left.qlen = 0; 503 cscanq->left.queue = cscanq->left.qtail = NULL; 504 DO_HEAD_DEQ(req, &cscanq->right); 505 } 506 } 507 RF_ASSERT(req); 508 cscanq->last_sector = req->sectorOffset; 509 return (req); 510 } 511 512 RF_DiskQueueData_t * 513 rf_SstfPeek(void *qptr) 514 { 515 RF_DiskQueueData_t *req; 516 RF_Sstf_t *sstfq; 517 518 sstfq = (RF_Sstf_t *) qptr; 519 520 if ((sstfq->left.queue == NULL) && (sstfq->right.queue == NULL)) { 521 req = closest_to_arm(&sstfq->lopri, sstfq->last_sector, &sstfq->dir, 522 sstfq->allow_reverse); 523 } else { 524 if (sstfq->left.queue == NULL) 525 req = sstfq->right.queue; 526 else { 527 if (sstfq->right.queue == NULL) 528 req = sstfq->left.queue; 529 else { 530 if (SNUM_DIFF(sstfq->last_sector, sstfq->right.queue->sectorOffset) 531 < SNUM_DIFF(sstfq->last_sector, sstfq->left.qtail->sectorOffset)) { 532 req = sstfq->right.queue; 533 } else { 534 req = sstfq->left.qtail; 535 } 536 } 537 } 538 } 539 if (req == NULL) { 540 RF_ASSERT(QSUM(sstfq) == 0); 541 } 542 return (req); 543 } 544 545 RF_DiskQueueData_t * 546 rf_ScanPeek(void *qptr) 547 { 548 RF_DiskQueueData_t *req; 549 RF_Sstf_t *scanq; 550 int dir; 551 552 scanq = (RF_Sstf_t *) qptr; 553 dir = scanq->dir; 554 555 if (scanq->left.queue == NULL) { 556 RF_ASSERT(scanq->left.qlen == 0); 557 if (scanq->right.queue == NULL) { 558 RF_ASSERT(scanq->right.qlen == 0); 559 if (scanq->lopri.queue == NULL) { 560 RF_ASSERT(scanq->lopri.qlen == 0); 561 return (NULL); 562 } 563 req = closest_to_arm(&scanq->lopri, scanq->last_sector, 564 &dir, scanq->allow_reverse); 565 } else { 566 req = scanq->right.queue; 567 } 568 } else 569 if (scanq->right.queue == NULL) { 570 RF_ASSERT(scanq->right.qlen == 0); 571 RF_ASSERT(scanq->left.queue); 572 req = scanq->left.qtail; 573 } else { 574 RF_ASSERT(scanq->right.queue); 575 RF_ASSERT(scanq->left.queue); 576 if (scanq->dir == DIR_RIGHT) { 577 req = scanq->right.queue; 578 } else { 579 req = scanq->left.qtail; 580 } 581 } 582 if (req == NULL) { 583 RF_ASSERT(QSUM(scanq) == 0); 584 } 585 return (req); 586 } 587 588 RF_DiskQueueData_t * 589 rf_CscanPeek(void *qptr) 590 { 591 RF_DiskQueueData_t *req; 592 RF_Sstf_t *cscanq; 593 594 cscanq = (RF_Sstf_t *) qptr; 595 596 RF_ASSERT(cscanq->dir == DIR_RIGHT); 597 if (cscanq->right.queue) { 598 req = cscanq->right.queue; 599 } else { 600 RF_ASSERT(cscanq->right.qlen == 0); 601 if (cscanq->left.queue == NULL) { 602 RF_ASSERT(cscanq->left.qlen == 0); 603 if (cscanq->lopri.queue == NULL) { 604 RF_ASSERT(cscanq->lopri.qlen == 0); 605 return (NULL); 606 } 607 req = closest_to_arm(&cscanq->lopri, cscanq->last_sector, 608 &cscanq->dir, cscanq->allow_reverse); 609 } else { 610 /* 611 * There's I/Os to the left of the arm. We'll end 612 * up swinging on back. 613 */ 614 req = cscanq->left.queue; 615 } 616 } 617 if (req == NULL) { 618 RF_ASSERT(QSUM(cscanq) == 0); 619 } 620 return (req); 621 } 622 623 int 624 rf_SstfPromote(void *qptr, RF_StripeNum_t parityStripeID, RF_ReconUnitNum_t which_ru) 625 { 626 RF_DiskQueueData_t *r, *next; 627 RF_Sstf_t *sstfq; 628 int n; 629 630 sstfq = (RF_Sstf_t *) qptr; 631 632 n = 0; 633 for (r = sstfq->lopri.queue; r; r = next) { 634 next = r->next; 635 #if RF_DEBUG_QUEUE 636 if (rf_sstfDebug || rf_scanDebug || rf_cscanDebug) { 637 printf("raid%d: check promote %lx\n", 638 r->raidPtr->raidid, (long) r); 639 } 640 #endif 641 if ((r->parityStripeID == parityStripeID) 642 && (r->which_ru == which_ru)) { 643 do_dequeue(&sstfq->lopri, r); 644 rf_SstfEnqueue(qptr, r, RF_IO_NORMAL_PRIORITY); 645 n++; 646 } 647 } 648 #if RF_DEBUG_QUEUE 649 if (rf_sstfDebug || rf_scanDebug || rf_cscanDebug) { 650 printf("raid%d: promoted %d matching I/Os queues are %d,%d,%d\n", 651 r->raidPtr->raidid, n, sstfq->left.qlen, 652 sstfq->right.qlen, sstfq->lopri.qlen); 653 } 654 #endif 655 return (n); 656 } 657