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/bio.h> 18 #include <sys/conf.h> 19 #include <sys/disk.h> 20 #include <sys/disklabel.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 case BIO_SETATTR: printf("cmd=setattr "); 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 * Seek sort for disks. 66 * 67 * The buf_queue keep two queues, sorted in ascending block order. The first 68 * queue holds those requests which are positioned after the current block 69 * (in the first request); the second, which starts at queue->switch_point, 70 * holds requests which came in after their block number was passed. Thus 71 * we implement a one way scan, retracting after reaching the end of the drive 72 * to the first request on the second queue, at which time it becomes the 73 * first queue. 74 * 75 * A one-way scan is natural because of the way UNIX read-ahead blocks are 76 * allocated. 77 */ 78 79 void 80 bioq_disksort(bioq, bp) 81 struct bio_queue_head *bioq; 82 struct bio *bp; 83 { 84 struct bio *bq; 85 struct bio *bn; 86 struct bio *be; 87 88 if (!atomic_cmpset_int(&bioq->busy, 0, 1)) 89 panic("Recursing in bioq_disksort()"); 90 be = TAILQ_LAST(&bioq->queue, bio_queue); 91 /* 92 * If the queue is empty or we are an 93 * ordered transaction, then it's easy. 94 */ 95 if ((bq = bioq_first(bioq)) == NULL) { 96 bioq_insert_tail(bioq, bp); 97 bioq->busy = 0; 98 return; 99 } else if (bioq->insert_point != NULL) { 100 101 /* 102 * A certain portion of the list is 103 * "locked" to preserve ordering, so 104 * we can only insert after the insert 105 * point. 106 */ 107 bq = bioq->insert_point; 108 } else { 109 110 /* 111 * If we lie before the last removed (currently active) 112 * request, and are not inserting ourselves into the 113 * "locked" portion of the list, then we must add ourselves 114 * to the second request list. 115 */ 116 if (bp->bio_pblkno < bioq->last_pblkno) { 117 118 bq = bioq->switch_point; 119 /* 120 * If we are starting a new secondary list, 121 * then it's easy. 122 */ 123 if (bq == NULL) { 124 bioq->switch_point = bp; 125 bioq_insert_tail(bioq, bp); 126 bioq->busy = 0; 127 return; 128 } 129 /* 130 * If we lie ahead of the current switch point, 131 * insert us before the switch point and move 132 * the switch point. 133 */ 134 if (bp->bio_pblkno < bq->bio_pblkno) { 135 bioq->switch_point = bp; 136 TAILQ_INSERT_BEFORE(bq, bp, bio_queue); 137 bioq->busy = 0; 138 return; 139 } 140 } else { 141 if (bioq->switch_point != NULL) 142 be = TAILQ_PREV(bioq->switch_point, 143 bio_queue, bio_queue); 144 /* 145 * If we lie between last_pblkno and bq, 146 * insert before bq. 147 */ 148 if (bp->bio_pblkno < bq->bio_pblkno) { 149 TAILQ_INSERT_BEFORE(bq, bp, bio_queue); 150 bioq->busy = 0; 151 return; 152 } 153 } 154 } 155 156 /* 157 * Request is at/after our current position in the list. 158 * Optimize for sequential I/O by seeing if we go at the tail. 159 */ 160 if (bp->bio_pblkno > be->bio_pblkno) { 161 TAILQ_INSERT_AFTER(&bioq->queue, be, bp, bio_queue); 162 bioq->busy = 0; 163 return; 164 } 165 166 /* Otherwise, insertion sort */ 167 while ((bn = TAILQ_NEXT(bq, bio_queue)) != NULL) { 168 169 /* 170 * We want to go after the current request if it is the end 171 * of the first request list, or if the next request is a 172 * larger cylinder than our request. 173 */ 174 if (bn == bioq->switch_point 175 || bp->bio_pblkno < bn->bio_pblkno) 176 break; 177 bq = bn; 178 } 179 TAILQ_INSERT_AFTER(&bioq->queue, bq, bp, bio_queue); 180 bioq->busy = 0; 181 } 182 183 184