subr_disk.c revision 163832
1116742Ssam/*-
2116904Ssam * ----------------------------------------------------------------------------
3170360Ssam * "THE BEER-WARE LICENSE" (Revision 42):
4116742Ssam * <phk@FreeBSD.ORG> wrote this file.  As long as you retain this notice you
5116742Ssam * can do whatever you want with this stuff. If we meet some day, and you think
6116742Ssam * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
7116742Ssam * ----------------------------------------------------------------------------
8116742Ssam */
9116742Ssam
10116904Ssam#include <sys/cdefs.h>
11116904Ssam__FBSDID("$FreeBSD: head/sys/kern/subr_disk.c 163832 2006-10-31 21:11:21Z pjd $");
12116904Ssam
13116904Ssam#include "opt_geom.h"
14116742Ssam
15116904Ssam#include <sys/param.h>
16116904Ssam#include <sys/systm.h>
17116904Ssam#include <sys/bio.h>
18116904Ssam#include <sys/conf.h>
19116904Ssam#include <sys/disk.h>
20116904Ssam#include <geom/geom_disk.h>
21116904Ssam
22116904Ssam/*-
23116904Ssam * Disk error is the preface to plaintive error messages
24116904Ssam * about failing disk transfers.  It prints messages of the form
25116742Ssam * 	"hp0g: BLABLABLA cmd=read fsbn 12345 of 12344-12347"
26116742Ssam * blkdone should be -1 if the position of the error is unknown.
27116742Ssam * The message is printed with printf.
28116742Ssam */
29116742Ssamvoid
30116742Ssamdisk_err(struct bio *bp, const char *what, int blkdone, int nl)
31116742Ssam{
32116742Ssam	daddr_t sn;
33116742Ssam
34116742Ssam	if (bp->bio_dev != NULL)
35138568Ssam		printf("%s: %s ", devtoname(bp->bio_dev), what);
36116742Ssam	else if (bp->bio_disk != NULL)
37116742Ssam		printf("%s%d: %s ",
38116742Ssam		    bp->bio_disk->d_name, bp->bio_disk->d_unit, what);
39116742Ssam	else
40116742Ssam		printf("disk??: %s ", what);
41116742Ssam	switch(bp->bio_cmd) {
42116742Ssam	case BIO_READ:		printf("cmd=read "); break;
43116742Ssam	case BIO_WRITE:		printf("cmd=write "); break;
44116742Ssam	case BIO_DELETE:	printf("cmd=delete "); break;
45116742Ssam	case BIO_GETATTR:	printf("cmd=getattr "); break;
46147221Ssam	case BIO_FLUSH:		printf("cmd=flush "); break;
47147221Ssam	default:		printf("cmd=%x ", bp->bio_cmd); break;
48147221Ssam	}
49147221Ssam	sn = bp->bio_pblkno;
50147221Ssam	if (bp->bio_bcount <= DEV_BSIZE) {
51147221Ssam		printf("fsbn %jd%s", (intmax_t)sn, nl ? "\n" : "");
52147221Ssam		return;
53147221Ssam	}
54147221Ssam	if (blkdone >= 0) {
55147221Ssam		sn += blkdone;
56159139Sdds		printf("fsbn %jd of ", (intmax_t)sn);
57159139Sdds	}
58159139Sdds	printf("%jd-%jd", (intmax_t)bp->bio_pblkno,
59159139Sdds	    (intmax_t)(bp->bio_pblkno + (bp->bio_bcount - 1) / DEV_BSIZE));
60159139Sdds	if (nl)
61159139Sdds		printf("\n");
62170530Ssam}
63170530Ssam
64138568Ssam/*
65138568Ssam * BIO queue implementation
66138568Ssam */
67170530Ssam
68170530Ssamvoid
69116742Ssambioq_init(struct bio_queue_head *head)
70138568Ssam{
71170530Ssam	TAILQ_INIT(&head->queue);
72138568Ssam	head->last_offset = 0;
73120104Ssam	head->insert_point = NULL;
74138568Ssam}
75148863Ssam
76170530Ssamvoid
77170530Ssambioq_remove(struct bio_queue_head *head, struct bio *bp)
78138568Ssam{
79172211Ssam	if (bp == head->insert_point) {
80138568Ssam		head->last_offset = bp->bio_offset;
81127876Ssam		head->insert_point = TAILQ_NEXT(bp, bio_queue);
82120481Ssam		if (head->insert_point == NULL) {
83116742Ssam			head->last_offset = 0;
84138568Ssam			head->insert_point = TAILQ_FIRST(&head->queue);
85116742Ssam		}
86116742Ssam	}
87138568Ssam	TAILQ_REMOVE(&head->queue, bp, bio_queue);
88138568Ssam}
89138568Ssam
90138568Ssamvoid
91170530Ssambioq_flush(struct bio_queue_head *head, struct devstat *stp, int error)
92138568Ssam{
93138568Ssam	struct bio *bp;
94138568Ssam
95138568Ssam	while ((bp = bioq_takefirst(head)) != NULL)
96138568Ssam		biofinish(bp, stp, error);
97138568Ssam}
98138568Ssam
99170530Ssamvoid
100170530Ssambioq_insert_head(struct bio_queue_head *head, struct bio *bp)
101148863Ssam{
102148863Ssam
103172062Ssam	if (TAILQ_EMPTY(&head->queue))
104172062Ssam		head->insert_point = bp;
105148863Ssam	TAILQ_INSERT_HEAD(&head->queue, bp, bio_queue);
106148863Ssam}
107148863Ssam
108148863Ssamvoid
109148863Ssambioq_insert_tail(struct bio_queue_head *head, struct bio *bp)
110148863Ssam{
111148863Ssam
112148863Ssam	if (TAILQ_EMPTY(&head->queue))
113138568Ssam		head->insert_point = bp;
114170530Ssam	TAILQ_INSERT_TAIL(&head->queue, bp, bio_queue);
115170530Ssam}
116170530Ssam
117138568Ssamstruct bio *
118138568Ssambioq_first(struct bio_queue_head *head)
119138568Ssam{
120138568Ssam
121138568Ssam	return (TAILQ_FIRST(&head->queue));
122138568Ssam}
123148863Ssam
124170530Ssamstruct bio *
125118887Ssambioq_takefirst(struct bio_queue_head *head)
126148863Ssam{
127138568Ssam	struct bio *bp;
128138568Ssam
129138568Ssam	bp = TAILQ_FIRST(&head->queue);
130138568Ssam	if (bp != NULL)
131148863Ssam		bioq_remove(head, bp);
132138568Ssam	return (bp);
133138568Ssam}
134138568Ssam
135138568Ssam/*
136138568Ssam * Seek sort for disks.
137138568Ssam *
138138568Ssam * The disksort algorithm sorts all requests in a single queue while keeping
139138568Ssam * track of the current position of the disk with insert_point and
140138568Ssam * last_offset.  last_offset is the offset of the last block sent to disk, or
141138568Ssam * 0 once we reach the end.  insert_point points to the first buf after
142138568Ssam * last_offset, and is used to slightly speed up insertions.  Blocks are
143138568Ssam * always sorted in ascending order and the queue always restarts at 0.
144138568Ssam * This implements the one-way scan which optimizes disk seek times.
145138568Ssam */
146138568Ssamvoid
147138568Ssambioq_disksort(bioq, bp)
148138568Ssam	struct bio_queue_head *bioq;
149138568Ssam	struct bio *bp;
150138568Ssam{
151138568Ssam	struct bio *bq;
152138568Ssam	struct bio *bn;
153138568Ssam
154138568Ssam	/*
155138568Ssam	 * If the queue is empty then it's easy.
156138568Ssam	 */
157138568Ssam	if (bioq_first(bioq) == NULL) {
158138568Ssam		bioq_insert_tail(bioq, bp);
159138568Ssam		return;
160138568Ssam	}
161138568Ssam	/*
162138568Ssam	 * Optimize for sequential I/O by seeing if we go at the tail.
163148863Ssam	 */
164116742Ssam	bq = TAILQ_LAST(&bioq->queue, bio_queue);
165116742Ssam	if (bp->bio_offset > bq->bio_offset) {
166116742Ssam		TAILQ_INSERT_AFTER(&bioq->queue, bq, bp, bio_queue);
167138568Ssam		return;
168116742Ssam	}
169116742Ssam	/*
170138568Ssam	 * Pick our scan start based on the last request.  A poor man's
171138568Ssam	 * binary search.
172138568Ssam	 */
173138568Ssam	if (bp->bio_offset >= bioq->last_offset) {
174140753Ssam		bq = bioq->insert_point;
175138568Ssam		/*
176170530Ssam		 * If we're before the next bio and after the last offset,
177138568Ssam		 * update insert_point;
178138568Ssam		 */
179116742Ssam		if (bp->bio_offset < bq->bio_offset) {
180116742Ssam			bioq->insert_point = bp;
181138568Ssam			TAILQ_INSERT_BEFORE(bq, bp, bio_queue);
182138568Ssam			return;
183138568Ssam		}
184138568Ssam	} else
185138568Ssam		bq = TAILQ_FIRST(&bioq->queue);
186148302Ssam	if (bp->bio_offset < bq->bio_offset) {
187138568Ssam		TAILQ_INSERT_BEFORE(bq, bp, bio_queue);
188148302Ssam		return;
189148302Ssam	}
190138568Ssam	/* Insertion sort */
191139528Ssam	while ((bn = TAILQ_NEXT(bq, bio_queue)) != NULL) {
192172062Ssam		if (bp->bio_offset < bn->bio_offset)
193138568Ssam			break;
194138568Ssam		bq = bn;
195138568Ssam	}
196148302Ssam	TAILQ_INSERT_AFTER(&bioq->queue, bq, bp, bio_queue);
197138568Ssam}
198172063Ssam