subr_disk.c revision 112941
150565Sphk/*
250565Sphk * ----------------------------------------------------------------------------
350565Sphk * "THE BEER-WARE LICENSE" (Revision 42):
450565Sphk * <phk@FreeBSD.ORG> wrote this file.  As long as you retain this notice you
550565Sphk * can do whatever you want with this stuff. If we meet some day, and you think
650565Sphk * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
750565Sphk * ----------------------------------------------------------------------------
850565Sphk *
950565Sphk * $FreeBSD: head/sys/kern/subr_disk.c 112941 2003-04-01 12:49:40Z phk $
1050565Sphk *
1150565Sphk */
1250565Sphk
1392074Sphk#include "opt_geom.h"
1492074Sphk
1550565Sphk#include <sys/param.h>
1650565Sphk#include <sys/systm.h>
1760041Sphk#include <sys/bio.h>
1850565Sphk#include <sys/conf.h>
1950565Sphk#include <sys/disk.h>
20103714Sphk#include <sys/disklabel.h>
2164880Sphk
22103675Sphk/*-
23103675Sphk * Disk error is the preface to plaintive error messages
24103675Sphk * about failing disk transfers.  It prints messages of the form
25103675Sphk * 	"hp0g: BLABLABLA cmd=read fsbn 12345 of 12344-12347"
26103675Sphk * blkdone should be -1 if the position of the error is unknown.
27103675Sphk * The message is printed with printf.
28103675Sphk */
29103675Sphkvoid
30103675Sphkdisk_err(struct bio *bp, const char *what, int blkdone, int nl)
31103675Sphk{
32103675Sphk	daddr_t sn;
33103675Sphk
34111808Sphk	if (bp->bio_dev != NULL)
35111808Sphk		printf("%s: %s ", devtoname(bp->bio_dev), what);
36111808Sphk	else if (bp->bio_disk != NULL)
37111808Sphk		printf("%s%d: %s ",
38111808Sphk		    bp->bio_disk->d_name, bp->bio_disk->d_unit, what);
39111808Sphk	else
40111808Sphk		printf("disk??: %s ", what);
41103675Sphk	switch(bp->bio_cmd) {
42105365Ssobomax	case BIO_READ:		printf("cmd=read "); break;
43105365Ssobomax	case BIO_WRITE:		printf("cmd=write "); break;
44105365Ssobomax	case BIO_DELETE:	printf("cmd=delete "); break;
45105365Ssobomax	case BIO_GETATTR:	printf("cmd=getattr "); break;
46105365Ssobomax	case BIO_SETATTR:	printf("cmd=setattr "); break;
47105365Ssobomax	default:		printf("cmd=%x ", bp->bio_cmd); break;
48103675Sphk	}
49103675Sphk	sn = bp->bio_blkno;
50103675Sphk	if (bp->bio_bcount <= DEV_BSIZE) {
51103675Sphk		printf("fsbn %jd%s", (intmax_t)sn, nl ? "\n" : "");
52103675Sphk		return;
53103675Sphk	}
54103675Sphk	if (blkdone >= 0) {
55103675Sphk		sn += blkdone;
56103675Sphk		printf("fsbn %jd of ", (intmax_t)sn);
57103675Sphk	}
58103675Sphk	printf("%jd-%jd", (intmax_t)bp->bio_blkno,
59103675Sphk	    (intmax_t)(bp->bio_blkno + (bp->bio_bcount - 1) / DEV_BSIZE));
60103675Sphk	if (nl)
61103675Sphk		printf("\n");
62103675Sphk}
63103683Sphk
64103683Sphk/*
65112846Sphk * BIO queue implementation
66112846Sphk */
67112846Sphk
68112846Sphkvoid
69112846Sphkbioq_init(struct bio_queue_head *head)
70112846Sphk{
71112846Sphk	TAILQ_INIT(&head->queue);
72112846Sphk	head->last_pblkno = 0;
73112846Sphk	head->insert_point = NULL;
74112846Sphk	head->switch_point = NULL;
75112846Sphk}
76112846Sphk
77112846Sphkvoid
78112846Sphkbioq_remove(struct bio_queue_head *head, struct bio *bp)
79112846Sphk{
80112846Sphk	if (bp == head->switch_point)
81112846Sphk		head->switch_point = TAILQ_NEXT(bp, bio_queue);
82112846Sphk	if (bp == head->insert_point) {
83112846Sphk		head->insert_point = TAILQ_PREV(bp, bio_queue, bio_queue);
84112846Sphk		if (head->insert_point == NULL)
85112846Sphk			head->last_pblkno = 0;
86112846Sphk	} else if (bp == TAILQ_FIRST(&head->queue))
87112846Sphk		head->last_pblkno = bp->bio_pblkno;
88112846Sphk	TAILQ_REMOVE(&head->queue, bp, bio_queue);
89112846Sphk	if (TAILQ_FIRST(&head->queue) == head->switch_point)
90112846Sphk		head->switch_point = NULL;
91112846Sphk}
92112941Sphk
93112846Sphkvoid
94112941Sphkbioq_flush(struct bio_queue_head *head, struct devstat *stp, int error)
95112941Sphk{
96112941Sphk	struct bio *bp;
97112941Sphk
98112941Sphk	for (;;) {
99112941Sphk		bp = bioq_first(head);
100112941Sphk		if (bp == NULL)
101112941Sphk			break;
102112941Sphk		bioq_remove(head, bp);
103112941Sphk		biofinish(bp, stp, ENXIO);
104112941Sphk	}
105112941Sphk}
106112941Sphk
107112941Sphkvoid
108112846Sphkbioq_insert_tail(struct bio_queue_head *head, struct bio *bp)
109112846Sphk{
110112846Sphk
111112846Sphk	TAILQ_INSERT_TAIL(&head->queue, bp, bio_queue);
112112846Sphk}
113112846Sphk
114112846Sphkstruct bio *
115112846Sphkbioq_first(struct bio_queue_head *head)
116112846Sphk{
117112846Sphk
118112846Sphk	return (TAILQ_FIRST(&head->queue));
119112846Sphk}
120112846Sphk
121112846Sphk
122112846Sphk/*
123103683Sphk * Seek sort for disks.
124103683Sphk *
125103683Sphk * The buf_queue keep two queues, sorted in ascending block order.  The first
126103683Sphk * queue holds those requests which are positioned after the current block
127103683Sphk * (in the first request); the second, which starts at queue->switch_point,
128103683Sphk * holds requests which came in after their block number was passed.  Thus
129103683Sphk * we implement a one way scan, retracting after reaching the end of the drive
130103683Sphk * to the first request on the second queue, at which time it becomes the
131103683Sphk * first queue.
132103683Sphk *
133103683Sphk * A one-way scan is natural because of the way UNIX read-ahead blocks are
134103683Sphk * allocated.
135103683Sphk */
136103683Sphk
137103683Sphkvoid
138103683Sphkbioq_disksort(bioq, bp)
139103683Sphk	struct bio_queue_head *bioq;
140103683Sphk	struct bio *bp;
141103683Sphk{
142103683Sphk	struct bio *bq;
143103683Sphk	struct bio *bn;
144103683Sphk	struct bio *be;
145103683Sphk
146103683Sphk	be = TAILQ_LAST(&bioq->queue, bio_queue);
147103683Sphk	/*
148103683Sphk	 * If the queue is empty or we are an
149103683Sphk	 * ordered transaction, then it's easy.
150103683Sphk	 */
151103683Sphk	if ((bq = bioq_first(bioq)) == NULL) {
152103683Sphk		bioq_insert_tail(bioq, bp);
153103683Sphk		return;
154103683Sphk	} else if (bioq->insert_point != NULL) {
155103683Sphk
156103683Sphk		/*
157103683Sphk		 * A certain portion of the list is
158103683Sphk		 * "locked" to preserve ordering, so
159103683Sphk		 * we can only insert after the insert
160103683Sphk		 * point.
161103683Sphk		 */
162103683Sphk		bq = bioq->insert_point;
163103683Sphk	} else {
164103683Sphk
165103683Sphk		/*
166103683Sphk		 * If we lie before the last removed (currently active)
167103683Sphk		 * request, and are not inserting ourselves into the
168103683Sphk		 * "locked" portion of the list, then we must add ourselves
169103683Sphk		 * to the second request list.
170103683Sphk		 */
171103683Sphk		if (bp->bio_pblkno < bioq->last_pblkno) {
172103683Sphk
173103683Sphk			bq = bioq->switch_point;
174103683Sphk			/*
175103683Sphk			 * If we are starting a new secondary list,
176103683Sphk			 * then it's easy.
177103683Sphk			 */
178103683Sphk			if (bq == NULL) {
179103683Sphk				bioq->switch_point = bp;
180103683Sphk				bioq_insert_tail(bioq, bp);
181103683Sphk				return;
182103683Sphk			}
183103683Sphk			/*
184103683Sphk			 * If we lie ahead of the current switch point,
185103683Sphk			 * insert us before the switch point and move
186103683Sphk			 * the switch point.
187103683Sphk			 */
188103683Sphk			if (bp->bio_pblkno < bq->bio_pblkno) {
189103683Sphk				bioq->switch_point = bp;
190103683Sphk				TAILQ_INSERT_BEFORE(bq, bp, bio_queue);
191103683Sphk				return;
192103683Sphk			}
193103683Sphk		} else {
194103683Sphk			if (bioq->switch_point != NULL)
195103683Sphk				be = TAILQ_PREV(bioq->switch_point,
196103683Sphk						bio_queue, bio_queue);
197103683Sphk			/*
198103683Sphk			 * If we lie between last_pblkno and bq,
199103683Sphk			 * insert before bq.
200103683Sphk			 */
201103683Sphk			if (bp->bio_pblkno < bq->bio_pblkno) {
202103683Sphk				TAILQ_INSERT_BEFORE(bq, bp, bio_queue);
203103683Sphk				return;
204103683Sphk			}
205103683Sphk		}
206103683Sphk	}
207103683Sphk
208103683Sphk	/*
209103683Sphk	 * Request is at/after our current position in the list.
210103683Sphk	 * Optimize for sequential I/O by seeing if we go at the tail.
211103683Sphk	 */
212103683Sphk	if (bp->bio_pblkno > be->bio_pblkno) {
213103683Sphk		TAILQ_INSERT_AFTER(&bioq->queue, be, bp, bio_queue);
214103683Sphk		return;
215103683Sphk	}
216103683Sphk
217103683Sphk	/* Otherwise, insertion sort */
218103683Sphk	while ((bn = TAILQ_NEXT(bq, bio_queue)) != NULL) {
219103683Sphk
220103683Sphk		/*
221103683Sphk		 * We want to go after the current request if it is the end
222103683Sphk		 * of the first request list, or if the next request is a
223103683Sphk		 * larger cylinder than our request.
224103683Sphk		 */
225103683Sphk		if (bn == bioq->switch_point
226103683Sphk		 || bp->bio_pblkno < bn->bio_pblkno)
227103683Sphk			break;
228103683Sphk		bq = bn;
229103683Sphk	}
230103683Sphk	TAILQ_INSERT_AFTER(&bioq->queue, bq, bp, bio_queue);
231103683Sphk}
232103683Sphk
233103683Sphk
234