subr_disk.c revision 113033
1/* 2 * ---------------------------------------------------------------------------- 3 * "THE BEER-WARE LICENSE" (Revision 42): 4 * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you 5 * can do whatever you want with this stuff. If we meet some day, and you think 6 * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp 7 * ---------------------------------------------------------------------------- 8 * 9 * $FreeBSD: head/sys/kern/subr_disk.c 113033 2003-04-03 19:22:32Z phk $ 10 * 11 */ 12 13#include "opt_geom.h" 14 15#include <sys/param.h> 16#include <sys/systm.h> 17#include <sys/bio.h> 18#include <sys/conf.h> 19#include <sys/disk.h> 20#include <sys/disklabel.h> 21#include <geom/geom_disk.h> 22 23/*- 24 * Disk error is the preface to plaintive error messages 25 * about failing disk transfers. It prints messages of the form 26 * "hp0g: BLABLABLA cmd=read fsbn 12345 of 12344-12347" 27 * blkdone should be -1 if the position of the error is unknown. 28 * The message is printed with printf. 29 */ 30void 31disk_err(struct bio *bp, const char *what, int blkdone, int nl) 32{ 33 daddr_t sn; 34 35 if (bp->bio_dev != NULL) 36 printf("%s: %s ", devtoname(bp->bio_dev), what); 37 else if (bp->bio_disk != NULL) 38 printf("%s%d: %s ", 39 bp->bio_disk->d_name, bp->bio_disk->d_unit, what); 40 else 41 printf("disk??: %s ", what); 42 switch(bp->bio_cmd) { 43 case BIO_READ: printf("cmd=read "); break; 44 case BIO_WRITE: printf("cmd=write "); break; 45 case BIO_DELETE: printf("cmd=delete "); break; 46 case BIO_GETATTR: printf("cmd=getattr "); break; 47 default: printf("cmd=%x ", bp->bio_cmd); break; 48 } 49 sn = bp->bio_blkno; 50 if (bp->bio_bcount <= DEV_BSIZE) { 51 printf("fsbn %jd%s", (intmax_t)sn, nl ? "\n" : ""); 52 return; 53 } 54 if (blkdone >= 0) { 55 sn += blkdone; 56 printf("fsbn %jd of ", (intmax_t)sn); 57 } 58 printf("%jd-%jd", (intmax_t)bp->bio_blkno, 59 (intmax_t)(bp->bio_blkno + (bp->bio_bcount - 1) / DEV_BSIZE)); 60 if (nl) 61 printf("\n"); 62} 63 64/* 65 * BIO queue implementation 66 */ 67 68void 69bioq_init(struct bio_queue_head *head) 70{ 71 TAILQ_INIT(&head->queue); 72 head->last_pblkno = 0; 73 head->insert_point = NULL; 74 head->switch_point = NULL; 75} 76 77void 78bioq_remove(struct bio_queue_head *head, struct bio *bp) 79{ 80 if (bp == head->switch_point) 81 head->switch_point = TAILQ_NEXT(bp, bio_queue); 82 if (bp == head->insert_point) { 83 head->insert_point = TAILQ_PREV(bp, bio_queue, bio_queue); 84 if (head->insert_point == NULL) 85 head->last_pblkno = 0; 86 } else if (bp == TAILQ_FIRST(&head->queue)) 87 head->last_pblkno = bp->bio_pblkno; 88 TAILQ_REMOVE(&head->queue, bp, bio_queue); 89 if (TAILQ_FIRST(&head->queue) == head->switch_point) 90 head->switch_point = NULL; 91} 92 93void 94bioq_flush(struct bio_queue_head *head, struct devstat *stp, int error) 95{ 96 struct bio *bp; 97 98 for (;;) { 99 bp = bioq_first(head); 100 if (bp == NULL) 101 break; 102 bioq_remove(head, bp); 103 biofinish(bp, stp, ENXIO); 104 } 105} 106 107void 108bioq_insert_tail(struct bio_queue_head *head, struct bio *bp) 109{ 110 111 TAILQ_INSERT_TAIL(&head->queue, bp, bio_queue); 112} 113 114struct bio * 115bioq_first(struct bio_queue_head *head) 116{ 117 118 return (TAILQ_FIRST(&head->queue)); 119} 120 121 122/* 123 * Seek sort for disks. 124 * 125 * The buf_queue keep two queues, sorted in ascending block order. The first 126 * queue holds those requests which are positioned after the current block 127 * (in the first request); the second, which starts at queue->switch_point, 128 * holds requests which came in after their block number was passed. Thus 129 * we implement a one way scan, retracting after reaching the end of the drive 130 * to the first request on the second queue, at which time it becomes the 131 * first queue. 132 * 133 * A one-way scan is natural because of the way UNIX read-ahead blocks are 134 * allocated. 135 */ 136 137void 138bioq_disksort(bioq, bp) 139 struct bio_queue_head *bioq; 140 struct bio *bp; 141{ 142 struct bio *bq; 143 struct bio *bn; 144 struct bio *be; 145 146 be = TAILQ_LAST(&bioq->queue, bio_queue); 147 /* 148 * If the queue is empty or we are an 149 * ordered transaction, then it's easy. 150 */ 151 if ((bq = bioq_first(bioq)) == NULL) { 152 bioq_insert_tail(bioq, bp); 153 return; 154 } else if (bioq->insert_point != NULL) { 155 156 /* 157 * A certain portion of the list is 158 * "locked" to preserve ordering, so 159 * we can only insert after the insert 160 * point. 161 */ 162 bq = bioq->insert_point; 163 } else { 164 165 /* 166 * If we lie before the last removed (currently active) 167 * request, and are not inserting ourselves into the 168 * "locked" portion of the list, then we must add ourselves 169 * to the second request list. 170 */ 171 if (bp->bio_pblkno < bioq->last_pblkno) { 172 173 bq = bioq->switch_point; 174 /* 175 * If we are starting a new secondary list, 176 * then it's easy. 177 */ 178 if (bq == NULL) { 179 bioq->switch_point = bp; 180 bioq_insert_tail(bioq, bp); 181 return; 182 } 183 /* 184 * If we lie ahead of the current switch point, 185 * insert us before the switch point and move 186 * the switch point. 187 */ 188 if (bp->bio_pblkno < bq->bio_pblkno) { 189 bioq->switch_point = bp; 190 TAILQ_INSERT_BEFORE(bq, bp, bio_queue); 191 return; 192 } 193 } else { 194 if (bioq->switch_point != NULL) 195 be = TAILQ_PREV(bioq->switch_point, 196 bio_queue, bio_queue); 197 /* 198 * If we lie between last_pblkno and bq, 199 * insert before bq. 200 */ 201 if (bp->bio_pblkno < bq->bio_pblkno) { 202 TAILQ_INSERT_BEFORE(bq, bp, bio_queue); 203 return; 204 } 205 } 206 } 207 208 /* 209 * Request is at/after our current position in the list. 210 * Optimize for sequential I/O by seeing if we go at the tail. 211 */ 212 if (bp->bio_pblkno > be->bio_pblkno) { 213 TAILQ_INSERT_AFTER(&bioq->queue, be, bp, bio_queue); 214 return; 215 } 216 217 /* Otherwise, insertion sort */ 218 while ((bn = TAILQ_NEXT(bq, bio_queue)) != NULL) { 219 220 /* 221 * We want to go after the current request if it is the end 222 * of the first request list, or if the next request is a 223 * larger cylinder than our request. 224 */ 225 if (bn == bioq->switch_point 226 || bp->bio_pblkno < bn->bio_pblkno) 227 break; 228 bq = bn; 229 } 230 TAILQ_INSERT_AFTER(&bioq->queue, bq, bp, bio_queue); 231} 232 233 234