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 case BIO_FLUSH
: printf("cmd=flush "); break;
47 default: printf("cmd=%x ", bp
->bio_cmd
); break;
50 if (bp
->bio_bcount
<= DEV_BSIZE
) {
51 printf("fsbn %jd%s", (intmax_t)sn
, nl
? "\n" : "");
56 printf("fsbn %jd of ", (intmax_t)sn
);
58 printf("%jd-%jd", (intmax_t)bp
->bio_pblkno
,
59 (intmax_t)(bp
->bio_pblkno
+ (bp
->bio_bcount
- 1) / DEV_BSIZE
));
65 * BIO queue implementation
69 bioq_init(struct bio_queue_head
*head
)
71 TAILQ_INIT(&head
->queue
);
72 head
->last_offset
= 0;
73 head
->insert_point
= NULL
;
77 bioq_remove(struct bio_queue_head
*head
, struct bio
*bp
)
79 if (bp
== head
->insert_point
) {
80 head
->last_offset
= bp
->bio_offset
;
81 head
->insert_point
= TAILQ_NEXT(bp
, bio_queue
);
82 if (head
->insert_point
== NULL
) {
83 head
->last_offset
= 0;
84 head
->insert_point
= TAILQ_FIRST(&head
->queue
);
87 TAILQ_REMOVE(&head
->queue
, bp
, bio_queue
);
91 bioq_flush(struct bio_queue_head
*head
, struct devstat
*stp
, int error
)
95 while ((bp
= bioq_takefirst(head
)) != NULL
)
96 biofinish(bp
, stp
, error
);
100 bioq_insert_head(struct bio_queue_head
*head
, struct bio
*bp
)
103 if (TAILQ_EMPTY(&head
->queue
))
104 head
->insert_point
= bp
;
105 TAILQ_INSERT_HEAD(&head
->queue
, bp
, bio_queue
);
109 bioq_insert_tail(struct bio_queue_head
*head
, struct bio
*bp
)
112 if (TAILQ_EMPTY(&head
->queue
))
113 head
->insert_point
= bp
;
114 TAILQ_INSERT_TAIL(&head
->queue
, bp
, bio_queue
);
118 bioq_first(struct bio_queue_head
*head
)
121 return (TAILQ_FIRST(&head
->queue
));
125 bioq_takefirst(struct bio_queue_head
*head
)
129 bp
= TAILQ_FIRST(&head
->queue
);
131 bioq_remove(head
, bp
);
136 * Seek sort for disks.
138 * The disksort algorithm sorts all requests in a single queue while keeping
139 * track of the current position of the disk with insert_point and
140 * last_offset. last_offset is the offset of the last block sent to disk, or
141 * 0 once we reach the end. insert_point points to the first buf after
142 * last_offset, and is used to slightly speed up insertions. Blocks are
143 * always sorted in ascending order and the queue always restarts at 0.
144 * This implements the one-way scan which optimizes disk seek times.
147 bioq_disksort(bioq
, bp
)
148 struct bio_queue_head
*bioq
;
155 * If the queue is empty then it's easy.
157 if (bioq_first(bioq
) == NULL
) {
158 bioq_insert_tail(bioq
, bp
);
162 * Optimize for sequential I/O by seeing if we go at the tail.
164 bq
= TAILQ_LAST(&bioq
->queue
, bio_queue
);
165 if (bp
->bio_offset
> bq
->bio_offset
) {
166 TAILQ_INSERT_AFTER(&bioq
->queue
, bq
, bp
, bio_queue
);
170 * Pick our scan start based on the last request. A poor man's
173 if (bp
->bio_offset
>= bioq
->last_offset
) {
174 bq
= bioq
->insert_point
;
176 * If we're before the next bio and after the last offset,
177 * update insert_point;
179 if (bp
->bio_offset
< bq
->bio_offset
) {
180 bioq
->insert_point
= bp
;
181 TAILQ_INSERT_BEFORE(bq
, bp
, bio_queue
);
185 bq
= TAILQ_FIRST(&bioq
->queue
);
186 if (bp
->bio_offset
< bq
->bio_offset
) {
187 TAILQ_INSERT_BEFORE(bq
, bp
, bio_queue
);
191 while ((bn
= TAILQ_NEXT(bq
, bio_queue
)) != NULL
) {
192 if (bp
->bio_offset
< bn
->bio_offset
)
196 TAILQ_INSERT_AFTER(&bioq
->queue
, bq
, bp
, bio_queue
);