subr_disk.c revision 113581
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: head/sys/kern/subr_disk.c 113581 2003-04-16 20:57:35Z phk $
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 <geom/geom_disk.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 */
29void
30disk_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	default:		printf("cmd=%x ", bp->bio_cmd); break;
47	}
48	sn = bp->bio_blkno;
49	if (bp->bio_bcount <= DEV_BSIZE) {
50		printf("fsbn %jd%s", (intmax_t)sn, nl ? "\n" : "");
51		return;
52	}
53	if (blkdone >= 0) {
54		sn += blkdone;
55		printf("fsbn %jd of ", (intmax_t)sn);
56	}
57	printf("%jd-%jd", (intmax_t)bp->bio_blkno,
58	    (intmax_t)(bp->bio_blkno + (bp->bio_bcount - 1) / DEV_BSIZE));
59	if (nl)
60		printf("\n");
61}
62
63/*
64 * BIO queue implementation
65 */
66
67void
68bioq_init(struct bio_queue_head *head)
69{
70	TAILQ_INIT(&head->queue);
71	head->last_pblkno = 0;
72	head->insert_point = NULL;
73	head->switch_point = NULL;
74}
75
76void
77bioq_remove(struct bio_queue_head *head, struct bio *bp)
78{
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_pblkno = 0;
85	} else if (bp == TAILQ_FIRST(&head->queue))
86		head->last_pblkno = bp->bio_pblkno;
87	TAILQ_REMOVE(&head->queue, bp, bio_queue);
88	if (TAILQ_FIRST(&head->queue) == head->switch_point)
89		head->switch_point = NULL;
90}
91
92void
93bioq_flush(struct bio_queue_head *head, struct devstat *stp, int error)
94{
95	struct bio *bp;
96
97	for (;;) {
98		bp = bioq_first(head);
99		if (bp == NULL)
100			break;
101		bioq_remove(head, bp);
102		biofinish(bp, stp, ENXIO);
103	}
104}
105
106void
107bioq_insert_tail(struct bio_queue_head *head, struct bio *bp)
108{
109
110	TAILQ_INSERT_TAIL(&head->queue, bp, bio_queue);
111}
112
113struct bio *
114bioq_first(struct bio_queue_head *head)
115{
116
117	return (TAILQ_FIRST(&head->queue));
118}
119
120
121/*
122 * Seek sort for disks.
123 *
124 * The buf_queue keep two queues, sorted in ascending block order.  The first
125 * queue holds those requests which are positioned after the current block
126 * (in the first request); the second, which starts at queue->switch_point,
127 * holds requests which came in after their block number was passed.  Thus
128 * we implement a one way scan, retracting after reaching the end of the drive
129 * to the first request on the second queue, at which time it becomes the
130 * first queue.
131 *
132 * A one-way scan is natural because of the way UNIX read-ahead blocks are
133 * allocated.
134 */
135
136void
137bioq_disksort(bioq, bp)
138	struct bio_queue_head *bioq;
139	struct bio *bp;
140{
141	struct bio *bq;
142	struct bio *bn;
143	struct bio *be;
144
145	be = TAILQ_LAST(&bioq->queue, bio_queue);
146	/*
147	 * If the queue is empty or we are an
148	 * ordered transaction, then it's easy.
149	 */
150	if ((bq = bioq_first(bioq)) == NULL) {
151		bioq_insert_tail(bioq, bp);
152		return;
153	} else if (bioq->insert_point != NULL) {
154
155		/*
156		 * A certain portion of the list is
157		 * "locked" to preserve ordering, so
158		 * we can only insert after the insert
159		 * point.
160		 */
161		bq = bioq->insert_point;
162	} else {
163
164		/*
165		 * If we lie before the last removed (currently active)
166		 * request, and are not inserting ourselves into the
167		 * "locked" portion of the list, then we must add ourselves
168		 * to the second request list.
169		 */
170		if (bp->bio_pblkno < bioq->last_pblkno) {
171
172			bq = bioq->switch_point;
173			/*
174			 * If we are starting a new secondary list,
175			 * then it's easy.
176			 */
177			if (bq == NULL) {
178				bioq->switch_point = bp;
179				bioq_insert_tail(bioq, bp);
180				return;
181			}
182			/*
183			 * If we lie ahead of the current switch point,
184			 * insert us before the switch point and move
185			 * the switch point.
186			 */
187			if (bp->bio_pblkno < bq->bio_pblkno) {
188				bioq->switch_point = bp;
189				TAILQ_INSERT_BEFORE(bq, bp, bio_queue);
190				return;
191			}
192		} else {
193			if (bioq->switch_point != NULL)
194				be = TAILQ_PREV(bioq->switch_point,
195						bio_queue, bio_queue);
196			/*
197			 * If we lie between last_pblkno and bq,
198			 * insert before bq.
199			 */
200			if (bp->bio_pblkno < bq->bio_pblkno) {
201				TAILQ_INSERT_BEFORE(bq, bp, bio_queue);
202				return;
203			}
204		}
205	}
206
207	/*
208	 * Request is at/after our current position in the list.
209	 * Optimize for sequential I/O by seeing if we go at the tail.
210	 */
211	if (bp->bio_pblkno > be->bio_pblkno) {
212		TAILQ_INSERT_AFTER(&bioq->queue, be, bp, bio_queue);
213		return;
214	}
215
216	/* Otherwise, insertion sort */
217	while ((bn = TAILQ_NEXT(bq, bio_queue)) != NULL) {
218
219		/*
220		 * We want to go after the current request if it is the end
221		 * of the first request list, or if the next request is a
222		 * larger cylinder than our request.
223		 */
224		if (bn == bioq->switch_point
225		 || bp->bio_pblkno < bn->bio_pblkno)
226			break;
227		bq = bn;
228	}
229	TAILQ_INSERT_AFTER(&bioq->queue, bq, bp, bio_queue);
230}
231
232
233