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$ 10 * 11 */ 12 13 #include "opt_geom.h" 14 15 #include <sys/param.h> 16 #include <sys/systm.h> 17 #include <sys/stdint.h> 18 #include <sys/bio.h> 19 #include <sys/conf.h> 20 #include <sys/disk.h> 21 #include <sys/disklabel.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 */ 30 void 31 disk_err(struct bio *bp, const char *what, int blkdone, int nl) 32 { 33 daddr_t sn; 34 35 printf("%s: %s ", devtoname(bp->bio_dev), what); 36 switch(bp->bio_cmd) { 37 case BIO_READ: printf("cmd=read "); break; 38 case BIO_WRITE: printf("cmd=write "); break; 39 case BIO_DELETE: printf("cmd=delete "); break; 40 case BIO_GETATTR: printf("cmd=getattr "); break; 41 case BIO_SETATTR: printf("cmd=setattr "); break; 42 default: printf("cmd=%x ", bp->bio_cmd); break; 43 } 44 sn = bp->bio_blkno; 45 if (bp->bio_bcount <= DEV_BSIZE) { 46 printf("fsbn %jd%s", (intmax_t)sn, nl ? "\n" : ""); 47 return; 48 } 49 if (blkdone >= 0) { 50 sn += blkdone; 51 printf("fsbn %jd of ", (intmax_t)sn); 52 } 53 printf("%jd-%jd", (intmax_t)bp->bio_blkno, 54 (intmax_t)(bp->bio_blkno + (bp->bio_bcount - 1) / DEV_BSIZE)); 55 if (nl) 56 printf("\n"); 57 } 58 59 /* 60 * Seek sort for disks. 61 * 62 * The buf_queue keep two queues, sorted in ascending block order. The first 63 * queue holds those requests which are positioned after the current block 64 * (in the first request); the second, which starts at queue->switch_point, 65 * holds requests which came in after their block number was passed. Thus 66 * we implement a one way scan, retracting after reaching the end of the drive 67 * to the first request on the second queue, at which time it becomes the 68 * first queue. 69 * 70 * A one-way scan is natural because of the way UNIX read-ahead blocks are 71 * allocated. 72 */ 73 74 void 75 bioq_disksort(bioq, bp) 76 struct bio_queue_head *bioq; 77 struct bio *bp; 78 { 79 struct bio *bq; 80 struct bio *bn; 81 struct bio *be; 82 83 if (!atomic_cmpset_int(&bioq->busy, 0, 1)) 84 panic("Recursing in bioq_disksort()"); 85 be = TAILQ_LAST(&bioq->queue, bio_queue); 86 /* 87 * If the queue is empty or we are an 88 * ordered transaction, then it's easy. 89 */ 90 if ((bq = bioq_first(bioq)) == NULL) { 91 bioq_insert_tail(bioq, bp); 92 bioq->busy = 0; 93 return; 94 } else if (bioq->insert_point != NULL) { 95 96 /* 97 * A certain portion of the list is 98 * "locked" to preserve ordering, so 99 * we can only insert after the insert 100 * point. 101 */ 102 bq = bioq->insert_point; 103 } else { 104 105 /* 106 * If we lie before the last removed (currently active) 107 * request, and are not inserting ourselves into the 108 * "locked" portion of the list, then we must add ourselves 109 * to the second request list. 110 */ 111 if (bp->bio_pblkno < bioq->last_pblkno) { 112 113 bq = bioq->switch_point; 114 /* 115 * If we are starting a new secondary list, 116 * then it's easy. 117 */ 118 if (bq == NULL) { 119 bioq->switch_point = bp; 120 bioq_insert_tail(bioq, bp); 121 bioq->busy = 0; 122 return; 123 } 124 /* 125 * If we lie ahead of the current switch point, 126 * insert us before the switch point and move 127 * the switch point. 128 */ 129 if (bp->bio_pblkno < bq->bio_pblkno) { 130 bioq->switch_point = bp; 131 TAILQ_INSERT_BEFORE(bq, bp, bio_queue); 132 bioq->busy = 0; 133 return; 134 } 135 } else { 136 if (bioq->switch_point != NULL) 137 be = TAILQ_PREV(bioq->switch_point, 138 bio_queue, bio_queue); 139 /* 140 * If we lie between last_pblkno and bq, 141 * insert before bq. 142 */ 143 if (bp->bio_pblkno < bq->bio_pblkno) { 144 TAILQ_INSERT_BEFORE(bq, bp, bio_queue); 145 bioq->busy = 0; 146 return; 147 } 148 } 149 } 150 151 /* 152 * Request is at/after our current position in the list. 153 * Optimize for sequential I/O by seeing if we go at the tail. 154 */ 155 if (bp->bio_pblkno > be->bio_pblkno) { 156 TAILQ_INSERT_AFTER(&bioq->queue, be, bp, bio_queue); 157 bioq->busy = 0; 158 return; 159 } 160 161 /* Otherwise, insertion sort */ 162 while ((bn = TAILQ_NEXT(bq, bio_queue)) != NULL) { 163 164 /* 165 * We want to go after the current request if it is the end 166 * of the first request list, or if the next request is a 167 * larger cylinder than our request. 168 */ 169 if (bn == bioq->switch_point 170 || bp->bio_pblkno < bn->bio_pblkno) 171 break; 172 bq = bn; 173 } 174 TAILQ_INSERT_AFTER(&bioq->queue, bq, bp, bio_queue); 175 bioq->busy = 0; 176 } 177 178 179