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