subr_disk.c revision 112941
150565Sphk/* 250565Sphk * ---------------------------------------------------------------------------- 350565Sphk * "THE BEER-WARE LICENSE" (Revision 42): 450565Sphk * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you 550565Sphk * can do whatever you want with this stuff. If we meet some day, and you think 650565Sphk * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp 750565Sphk * ---------------------------------------------------------------------------- 850565Sphk * 950565Sphk * $FreeBSD: head/sys/kern/subr_disk.c 112941 2003-04-01 12:49:40Z phk $ 1050565Sphk * 1150565Sphk */ 1250565Sphk 1392074Sphk#include "opt_geom.h" 1492074Sphk 1550565Sphk#include <sys/param.h> 1650565Sphk#include <sys/systm.h> 1760041Sphk#include <sys/bio.h> 1850565Sphk#include <sys/conf.h> 1950565Sphk#include <sys/disk.h> 20103714Sphk#include <sys/disklabel.h> 2164880Sphk 22103675Sphk/*- 23103675Sphk * Disk error is the preface to plaintive error messages 24103675Sphk * about failing disk transfers. It prints messages of the form 25103675Sphk * "hp0g: BLABLABLA cmd=read fsbn 12345 of 12344-12347" 26103675Sphk * blkdone should be -1 if the position of the error is unknown. 27103675Sphk * The message is printed with printf. 28103675Sphk */ 29103675Sphkvoid 30103675Sphkdisk_err(struct bio *bp, const char *what, int blkdone, int nl) 31103675Sphk{ 32103675Sphk daddr_t sn; 33103675Sphk 34111808Sphk if (bp->bio_dev != NULL) 35111808Sphk printf("%s: %s ", devtoname(bp->bio_dev), what); 36111808Sphk else if (bp->bio_disk != NULL) 37111808Sphk printf("%s%d: %s ", 38111808Sphk bp->bio_disk->d_name, bp->bio_disk->d_unit, what); 39111808Sphk else 40111808Sphk printf("disk??: %s ", what); 41103675Sphk switch(bp->bio_cmd) { 42105365Ssobomax case BIO_READ: printf("cmd=read "); break; 43105365Ssobomax case BIO_WRITE: printf("cmd=write "); break; 44105365Ssobomax case BIO_DELETE: printf("cmd=delete "); break; 45105365Ssobomax case BIO_GETATTR: printf("cmd=getattr "); break; 46105365Ssobomax case BIO_SETATTR: printf("cmd=setattr "); break; 47105365Ssobomax default: printf("cmd=%x ", bp->bio_cmd); break; 48103675Sphk } 49103675Sphk sn = bp->bio_blkno; 50103675Sphk if (bp->bio_bcount <= DEV_BSIZE) { 51103675Sphk printf("fsbn %jd%s", (intmax_t)sn, nl ? "\n" : ""); 52103675Sphk return; 53103675Sphk } 54103675Sphk if (blkdone >= 0) { 55103675Sphk sn += blkdone; 56103675Sphk printf("fsbn %jd of ", (intmax_t)sn); 57103675Sphk } 58103675Sphk printf("%jd-%jd", (intmax_t)bp->bio_blkno, 59103675Sphk (intmax_t)(bp->bio_blkno + (bp->bio_bcount - 1) / DEV_BSIZE)); 60103675Sphk if (nl) 61103675Sphk printf("\n"); 62103675Sphk} 63103683Sphk 64103683Sphk/* 65112846Sphk * BIO queue implementation 66112846Sphk */ 67112846Sphk 68112846Sphkvoid 69112846Sphkbioq_init(struct bio_queue_head *head) 70112846Sphk{ 71112846Sphk TAILQ_INIT(&head->queue); 72112846Sphk head->last_pblkno = 0; 73112846Sphk head->insert_point = NULL; 74112846Sphk head->switch_point = NULL; 75112846Sphk} 76112846Sphk 77112846Sphkvoid 78112846Sphkbioq_remove(struct bio_queue_head *head, struct bio *bp) 79112846Sphk{ 80112846Sphk if (bp == head->switch_point) 81112846Sphk head->switch_point = TAILQ_NEXT(bp, bio_queue); 82112846Sphk if (bp == head->insert_point) { 83112846Sphk head->insert_point = TAILQ_PREV(bp, bio_queue, bio_queue); 84112846Sphk if (head->insert_point == NULL) 85112846Sphk head->last_pblkno = 0; 86112846Sphk } else if (bp == TAILQ_FIRST(&head->queue)) 87112846Sphk head->last_pblkno = bp->bio_pblkno; 88112846Sphk TAILQ_REMOVE(&head->queue, bp, bio_queue); 89112846Sphk if (TAILQ_FIRST(&head->queue) == head->switch_point) 90112846Sphk head->switch_point = NULL; 91112846Sphk} 92112941Sphk 93112846Sphkvoid 94112941Sphkbioq_flush(struct bio_queue_head *head, struct devstat *stp, int error) 95112941Sphk{ 96112941Sphk struct bio *bp; 97112941Sphk 98112941Sphk for (;;) { 99112941Sphk bp = bioq_first(head); 100112941Sphk if (bp == NULL) 101112941Sphk break; 102112941Sphk bioq_remove(head, bp); 103112941Sphk biofinish(bp, stp, ENXIO); 104112941Sphk } 105112941Sphk} 106112941Sphk 107112941Sphkvoid 108112846Sphkbioq_insert_tail(struct bio_queue_head *head, struct bio *bp) 109112846Sphk{ 110112846Sphk 111112846Sphk TAILQ_INSERT_TAIL(&head->queue, bp, bio_queue); 112112846Sphk} 113112846Sphk 114112846Sphkstruct bio * 115112846Sphkbioq_first(struct bio_queue_head *head) 116112846Sphk{ 117112846Sphk 118112846Sphk return (TAILQ_FIRST(&head->queue)); 119112846Sphk} 120112846Sphk 121112846Sphk 122112846Sphk/* 123103683Sphk * Seek sort for disks. 124103683Sphk * 125103683Sphk * The buf_queue keep two queues, sorted in ascending block order. The first 126103683Sphk * queue holds those requests which are positioned after the current block 127103683Sphk * (in the first request); the second, which starts at queue->switch_point, 128103683Sphk * holds requests which came in after their block number was passed. Thus 129103683Sphk * we implement a one way scan, retracting after reaching the end of the drive 130103683Sphk * to the first request on the second queue, at which time it becomes the 131103683Sphk * first queue. 132103683Sphk * 133103683Sphk * A one-way scan is natural because of the way UNIX read-ahead blocks are 134103683Sphk * allocated. 135103683Sphk */ 136103683Sphk 137103683Sphkvoid 138103683Sphkbioq_disksort(bioq, bp) 139103683Sphk struct bio_queue_head *bioq; 140103683Sphk struct bio *bp; 141103683Sphk{ 142103683Sphk struct bio *bq; 143103683Sphk struct bio *bn; 144103683Sphk struct bio *be; 145103683Sphk 146103683Sphk be = TAILQ_LAST(&bioq->queue, bio_queue); 147103683Sphk /* 148103683Sphk * If the queue is empty or we are an 149103683Sphk * ordered transaction, then it's easy. 150103683Sphk */ 151103683Sphk if ((bq = bioq_first(bioq)) == NULL) { 152103683Sphk bioq_insert_tail(bioq, bp); 153103683Sphk return; 154103683Sphk } else if (bioq->insert_point != NULL) { 155103683Sphk 156103683Sphk /* 157103683Sphk * A certain portion of the list is 158103683Sphk * "locked" to preserve ordering, so 159103683Sphk * we can only insert after the insert 160103683Sphk * point. 161103683Sphk */ 162103683Sphk bq = bioq->insert_point; 163103683Sphk } else { 164103683Sphk 165103683Sphk /* 166103683Sphk * If we lie before the last removed (currently active) 167103683Sphk * request, and are not inserting ourselves into the 168103683Sphk * "locked" portion of the list, then we must add ourselves 169103683Sphk * to the second request list. 170103683Sphk */ 171103683Sphk if (bp->bio_pblkno < bioq->last_pblkno) { 172103683Sphk 173103683Sphk bq = bioq->switch_point; 174103683Sphk /* 175103683Sphk * If we are starting a new secondary list, 176103683Sphk * then it's easy. 177103683Sphk */ 178103683Sphk if (bq == NULL) { 179103683Sphk bioq->switch_point = bp; 180103683Sphk bioq_insert_tail(bioq, bp); 181103683Sphk return; 182103683Sphk } 183103683Sphk /* 184103683Sphk * If we lie ahead of the current switch point, 185103683Sphk * insert us before the switch point and move 186103683Sphk * the switch point. 187103683Sphk */ 188103683Sphk if (bp->bio_pblkno < bq->bio_pblkno) { 189103683Sphk bioq->switch_point = bp; 190103683Sphk TAILQ_INSERT_BEFORE(bq, bp, bio_queue); 191103683Sphk return; 192103683Sphk } 193103683Sphk } else { 194103683Sphk if (bioq->switch_point != NULL) 195103683Sphk be = TAILQ_PREV(bioq->switch_point, 196103683Sphk bio_queue, bio_queue); 197103683Sphk /* 198103683Sphk * If we lie between last_pblkno and bq, 199103683Sphk * insert before bq. 200103683Sphk */ 201103683Sphk if (bp->bio_pblkno < bq->bio_pblkno) { 202103683Sphk TAILQ_INSERT_BEFORE(bq, bp, bio_queue); 203103683Sphk return; 204103683Sphk } 205103683Sphk } 206103683Sphk } 207103683Sphk 208103683Sphk /* 209103683Sphk * Request is at/after our current position in the list. 210103683Sphk * Optimize for sequential I/O by seeing if we go at the tail. 211103683Sphk */ 212103683Sphk if (bp->bio_pblkno > be->bio_pblkno) { 213103683Sphk TAILQ_INSERT_AFTER(&bioq->queue, be, bp, bio_queue); 214103683Sphk return; 215103683Sphk } 216103683Sphk 217103683Sphk /* Otherwise, insertion sort */ 218103683Sphk while ((bn = TAILQ_NEXT(bq, bio_queue)) != NULL) { 219103683Sphk 220103683Sphk /* 221103683Sphk * We want to go after the current request if it is the end 222103683Sphk * of the first request list, or if the next request is a 223103683Sphk * larger cylinder than our request. 224103683Sphk */ 225103683Sphk if (bn == bioq->switch_point 226103683Sphk || bp->bio_pblkno < bn->bio_pblkno) 227103683Sphk break; 228103683Sphk bq = bn; 229103683Sphk } 230103683Sphk TAILQ_INSERT_AFTER(&bioq->queue, bq, bp, bio_queue); 231103683Sphk} 232103683Sphk 233103683Sphk 234