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