subr_disk.c revision 163832
1116742Ssam/*- 2116904Ssam * ---------------------------------------------------------------------------- 3170360Ssam * "THE BEER-WARE LICENSE" (Revision 42): 4116742Ssam * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you 5116742Ssam * can do whatever you want with this stuff. If we meet some day, and you think 6116742Ssam * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp 7116742Ssam * ---------------------------------------------------------------------------- 8116742Ssam */ 9116742Ssam 10116904Ssam#include <sys/cdefs.h> 11116904Ssam__FBSDID("$FreeBSD: head/sys/kern/subr_disk.c 163832 2006-10-31 21:11:21Z pjd $"); 12116904Ssam 13116904Ssam#include "opt_geom.h" 14116742Ssam 15116904Ssam#include <sys/param.h> 16116904Ssam#include <sys/systm.h> 17116904Ssam#include <sys/bio.h> 18116904Ssam#include <sys/conf.h> 19116904Ssam#include <sys/disk.h> 20116904Ssam#include <geom/geom_disk.h> 21116904Ssam 22116904Ssam/*- 23116904Ssam * Disk error is the preface to plaintive error messages 24116904Ssam * about failing disk transfers. It prints messages of the form 25116742Ssam * "hp0g: BLABLABLA cmd=read fsbn 12345 of 12344-12347" 26116742Ssam * blkdone should be -1 if the position of the error is unknown. 27116742Ssam * The message is printed with printf. 28116742Ssam */ 29116742Ssamvoid 30116742Ssamdisk_err(struct bio *bp, const char *what, int blkdone, int nl) 31116742Ssam{ 32116742Ssam daddr_t sn; 33116742Ssam 34116742Ssam if (bp->bio_dev != NULL) 35138568Ssam printf("%s: %s ", devtoname(bp->bio_dev), what); 36116742Ssam else if (bp->bio_disk != NULL) 37116742Ssam printf("%s%d: %s ", 38116742Ssam bp->bio_disk->d_name, bp->bio_disk->d_unit, what); 39116742Ssam else 40116742Ssam printf("disk??: %s ", what); 41116742Ssam switch(bp->bio_cmd) { 42116742Ssam case BIO_READ: printf("cmd=read "); break; 43116742Ssam case BIO_WRITE: printf("cmd=write "); break; 44116742Ssam case BIO_DELETE: printf("cmd=delete "); break; 45116742Ssam case BIO_GETATTR: printf("cmd=getattr "); break; 46147221Ssam case BIO_FLUSH: printf("cmd=flush "); break; 47147221Ssam default: printf("cmd=%x ", bp->bio_cmd); break; 48147221Ssam } 49147221Ssam sn = bp->bio_pblkno; 50147221Ssam if (bp->bio_bcount <= DEV_BSIZE) { 51147221Ssam printf("fsbn %jd%s", (intmax_t)sn, nl ? "\n" : ""); 52147221Ssam return; 53147221Ssam } 54147221Ssam if (blkdone >= 0) { 55147221Ssam sn += blkdone; 56159139Sdds printf("fsbn %jd of ", (intmax_t)sn); 57159139Sdds } 58159139Sdds printf("%jd-%jd", (intmax_t)bp->bio_pblkno, 59159139Sdds (intmax_t)(bp->bio_pblkno + (bp->bio_bcount - 1) / DEV_BSIZE)); 60159139Sdds if (nl) 61159139Sdds printf("\n"); 62170530Ssam} 63170530Ssam 64138568Ssam/* 65138568Ssam * BIO queue implementation 66138568Ssam */ 67170530Ssam 68170530Ssamvoid 69116742Ssambioq_init(struct bio_queue_head *head) 70138568Ssam{ 71170530Ssam TAILQ_INIT(&head->queue); 72138568Ssam head->last_offset = 0; 73120104Ssam head->insert_point = NULL; 74138568Ssam} 75148863Ssam 76170530Ssamvoid 77170530Ssambioq_remove(struct bio_queue_head *head, struct bio *bp) 78138568Ssam{ 79172211Ssam if (bp == head->insert_point) { 80138568Ssam head->last_offset = bp->bio_offset; 81127876Ssam head->insert_point = TAILQ_NEXT(bp, bio_queue); 82120481Ssam if (head->insert_point == NULL) { 83116742Ssam head->last_offset = 0; 84138568Ssam head->insert_point = TAILQ_FIRST(&head->queue); 85116742Ssam } 86116742Ssam } 87138568Ssam TAILQ_REMOVE(&head->queue, bp, bio_queue); 88138568Ssam} 89138568Ssam 90138568Ssamvoid 91170530Ssambioq_flush(struct bio_queue_head *head, struct devstat *stp, int error) 92138568Ssam{ 93138568Ssam struct bio *bp; 94138568Ssam 95138568Ssam while ((bp = bioq_takefirst(head)) != NULL) 96138568Ssam biofinish(bp, stp, error); 97138568Ssam} 98138568Ssam 99170530Ssamvoid 100170530Ssambioq_insert_head(struct bio_queue_head *head, struct bio *bp) 101148863Ssam{ 102148863Ssam 103172062Ssam if (TAILQ_EMPTY(&head->queue)) 104172062Ssam head->insert_point = bp; 105148863Ssam TAILQ_INSERT_HEAD(&head->queue, bp, bio_queue); 106148863Ssam} 107148863Ssam 108148863Ssamvoid 109148863Ssambioq_insert_tail(struct bio_queue_head *head, struct bio *bp) 110148863Ssam{ 111148863Ssam 112148863Ssam if (TAILQ_EMPTY(&head->queue)) 113138568Ssam head->insert_point = bp; 114170530Ssam TAILQ_INSERT_TAIL(&head->queue, bp, bio_queue); 115170530Ssam} 116170530Ssam 117138568Ssamstruct bio * 118138568Ssambioq_first(struct bio_queue_head *head) 119138568Ssam{ 120138568Ssam 121138568Ssam return (TAILQ_FIRST(&head->queue)); 122138568Ssam} 123148863Ssam 124170530Ssamstruct bio * 125118887Ssambioq_takefirst(struct bio_queue_head *head) 126148863Ssam{ 127138568Ssam struct bio *bp; 128138568Ssam 129138568Ssam bp = TAILQ_FIRST(&head->queue); 130138568Ssam if (bp != NULL) 131148863Ssam bioq_remove(head, bp); 132138568Ssam return (bp); 133138568Ssam} 134138568Ssam 135138568Ssam/* 136138568Ssam * Seek sort for disks. 137138568Ssam * 138138568Ssam * The disksort algorithm sorts all requests in a single queue while keeping 139138568Ssam * track of the current position of the disk with insert_point and 140138568Ssam * last_offset. last_offset is the offset of the last block sent to disk, or 141138568Ssam * 0 once we reach the end. insert_point points to the first buf after 142138568Ssam * last_offset, and is used to slightly speed up insertions. Blocks are 143138568Ssam * always sorted in ascending order and the queue always restarts at 0. 144138568Ssam * This implements the one-way scan which optimizes disk seek times. 145138568Ssam */ 146138568Ssamvoid 147138568Ssambioq_disksort(bioq, bp) 148138568Ssam struct bio_queue_head *bioq; 149138568Ssam struct bio *bp; 150138568Ssam{ 151138568Ssam struct bio *bq; 152138568Ssam struct bio *bn; 153138568Ssam 154138568Ssam /* 155138568Ssam * If the queue is empty then it's easy. 156138568Ssam */ 157138568Ssam if (bioq_first(bioq) == NULL) { 158138568Ssam bioq_insert_tail(bioq, bp); 159138568Ssam return; 160138568Ssam } 161138568Ssam /* 162138568Ssam * Optimize for sequential I/O by seeing if we go at the tail. 163148863Ssam */ 164116742Ssam bq = TAILQ_LAST(&bioq->queue, bio_queue); 165116742Ssam if (bp->bio_offset > bq->bio_offset) { 166116742Ssam TAILQ_INSERT_AFTER(&bioq->queue, bq, bp, bio_queue); 167138568Ssam return; 168116742Ssam } 169116742Ssam /* 170138568Ssam * Pick our scan start based on the last request. A poor man's 171138568Ssam * binary search. 172138568Ssam */ 173138568Ssam if (bp->bio_offset >= bioq->last_offset) { 174140753Ssam bq = bioq->insert_point; 175138568Ssam /* 176170530Ssam * If we're before the next bio and after the last offset, 177138568Ssam * update insert_point; 178138568Ssam */ 179116742Ssam if (bp->bio_offset < bq->bio_offset) { 180116742Ssam bioq->insert_point = bp; 181138568Ssam TAILQ_INSERT_BEFORE(bq, bp, bio_queue); 182138568Ssam return; 183138568Ssam } 184138568Ssam } else 185138568Ssam bq = TAILQ_FIRST(&bioq->queue); 186148302Ssam if (bp->bio_offset < bq->bio_offset) { 187138568Ssam TAILQ_INSERT_BEFORE(bq, bp, bio_queue); 188148302Ssam return; 189148302Ssam } 190138568Ssam /* Insertion sort */ 191139528Ssam while ((bn = TAILQ_NEXT(bq, bio_queue)) != NULL) { 192172062Ssam if (bp->bio_offset < bn->bio_offset) 193138568Ssam break; 194138568Ssam bq = bn; 195138568Ssam } 196148302Ssam TAILQ_INSERT_AFTER(&bioq->queue, bq, bp, bio_queue); 197138568Ssam} 198172063Ssam