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 * ----------------------------------------------------------------------------
10 #include <sys/cdefs.h>
11 __FBSDID("$FreeBSD$");
15 #include <sys/param.h>
16 #include <sys/systm.h>
20 #include <geom/geom_disk.h>
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.
30 disk_err(struct bio *bp, const char *what, int blkdone, int nl)
34 if (bp->bio_dev != NULL)
35 printf("%s: %s ", devtoname(bp->bio_dev), what);
36 else if (bp->bio_disk != NULL)
38 bp->bio_disk->d_name, bp->bio_disk->d_unit, what);
40 printf("disk??: %s ", what);
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;
49 if (bp->bio_bcount <= DEV_BSIZE) {
50 printf("fsbn %jd%s", (intmax_t)sn, nl ? "\n" : "");
55 printf("fsbn %jd of ", (intmax_t)sn);
57 printf("%jd-%jd", (intmax_t)bp->bio_pblkno,
58 (intmax_t)(bp->bio_pblkno + (bp->bio_bcount - 1) / DEV_BSIZE));
64 * BIO queue implementation
68 bioq_init(struct bio_queue_head *head)
70 TAILQ_INIT(&head->queue);
71 head->last_offset = 0;
72 head->insert_point = NULL;
73 head->switch_point = NULL;
77 bioq_remove(struct bio_queue_head *head, struct bio *bp)
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;
93 bioq_flush(struct bio_queue_head *head, struct devstat *stp, int error)
97 while ((bp = bioq_takefirst(head)) != NULL)
98 biofinish(bp, stp, error);
102 bioq_insert_head(struct bio_queue_head *head, struct bio *bp)
105 TAILQ_INSERT_HEAD(&head->queue, bp, bio_queue);
109 bioq_insert_tail(struct bio_queue_head *head, struct bio *bp)
112 TAILQ_INSERT_TAIL(&head->queue, bp, bio_queue);
116 bioq_first(struct bio_queue_head *head)
119 return (TAILQ_FIRST(&head->queue));
123 bioq_takefirst(struct bio_queue_head *head)
127 bp = TAILQ_FIRST(&head->queue);
129 bioq_remove(head, bp);
134 * Seek sort for disks.
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
144 * A one-way scan is natural because of the way UNIX read-ahead blocks are
149 bioq_disksort(bioq, bp)
150 struct bio_queue_head *bioq;
157 be = TAILQ_LAST(&bioq->queue, bio_queue);
159 * If the queue is empty or we are an
160 * ordered transaction, then it's easy.
162 if ((bq = bioq_first(bioq)) == NULL) {
163 bioq_insert_tail(bioq, bp);
165 } else if (bioq->insert_point != NULL) {
168 * A certain portion of the list is
169 * "locked" to preserve ordering, so
170 * we can only insert after the insert
173 bq = bioq->insert_point;
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.
182 if (bp->bio_offset < bioq->last_offset) {
184 bq = bioq->switch_point;
186 * If we are starting a new secondary list,
190 bioq->switch_point = bp;
191 bioq_insert_tail(bioq, bp);
195 * If we lie ahead of the current switch point,
196 * insert us before the switch point and move
199 if (bp->bio_offset < bq->bio_offset) {
200 bioq->switch_point = bp;
201 TAILQ_INSERT_BEFORE(bq, bp, bio_queue);
205 if (bioq->switch_point != NULL)
206 be = TAILQ_PREV(bioq->switch_point,
207 bio_queue, bio_queue);
209 * If we lie between last_offset and bq,
212 if (bp->bio_offset < bq->bio_offset) {
213 TAILQ_INSERT_BEFORE(bq, bp, bio_queue);
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.
223 if (bp->bio_offset > be->bio_offset) {
224 TAILQ_INSERT_AFTER(&bioq->queue, be, bp, bio_queue);
228 /* Otherwise, insertion sort */
229 while ((bn = TAILQ_NEXT(bq, bio_queue)) != NULL) {
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.
236 if (bn == bioq->switch_point
237 || bp->bio_offset < bn->bio_offset)
241 TAILQ_INSERT_AFTER(&bioq->queue, bq, bp, bio_queue);