xref: /freebsd/sys/kern/subr_disk.c (revision bf4843166f2ed50e6435a77b38a011945de0a6f6)
1da9e4f55SPoul-Henning Kamp /*
2da9e4f55SPoul-Henning Kamp  * ----------------------------------------------------------------------------
3da9e4f55SPoul-Henning Kamp  * "THE BEER-WARE LICENSE" (Revision 42):
4da9e4f55SPoul-Henning Kamp  * <phk@FreeBSD.ORG> wrote this file.  As long as you retain this notice you
5da9e4f55SPoul-Henning Kamp  * can do whatever you want with this stuff. If we meet some day, and you think
6da9e4f55SPoul-Henning Kamp  * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
7da9e4f55SPoul-Henning Kamp  * ----------------------------------------------------------------------------
8da9e4f55SPoul-Henning Kamp  */
9da9e4f55SPoul-Henning Kamp 
10677b542eSDavid E. O'Brien #include <sys/cdefs.h>
11677b542eSDavid E. O'Brien __FBSDID("$FreeBSD$");
12677b542eSDavid E. O'Brien 
13417fb7f6SPoul-Henning Kamp #include "opt_geom.h"
14417fb7f6SPoul-Henning Kamp 
15da9e4f55SPoul-Henning Kamp #include <sys/param.h>
16da9e4f55SPoul-Henning Kamp #include <sys/systm.h>
179626b608SPoul-Henning Kamp #include <sys/bio.h>
18da9e4f55SPoul-Henning Kamp #include <sys/conf.h>
19da9e4f55SPoul-Henning Kamp #include <sys/disk.h>
2081750927SPoul-Henning Kamp #include <geom/geom_disk.h>
21f90c382cSPoul-Henning Kamp 
22f90c382cSPoul-Henning Kamp /*-
23f90c382cSPoul-Henning Kamp  * Disk error is the preface to plaintive error messages
24f90c382cSPoul-Henning Kamp  * about failing disk transfers.  It prints messages of the form
25f90c382cSPoul-Henning Kamp  * 	"hp0g: BLABLABLA cmd=read fsbn 12345 of 12344-12347"
26f90c382cSPoul-Henning Kamp  * blkdone should be -1 if the position of the error is unknown.
27f90c382cSPoul-Henning Kamp  * The message is printed with printf.
28f90c382cSPoul-Henning Kamp  */
29f90c382cSPoul-Henning Kamp void
30f90c382cSPoul-Henning Kamp disk_err(struct bio *bp, const char *what, int blkdone, int nl)
31f90c382cSPoul-Henning Kamp {
32f90c382cSPoul-Henning Kamp 	daddr_t sn;
33f90c382cSPoul-Henning Kamp 
34a9463ba8SPoul-Henning Kamp 	if (bp->bio_dev != NULL)
35f90c382cSPoul-Henning Kamp 		printf("%s: %s ", devtoname(bp->bio_dev), what);
36a9463ba8SPoul-Henning Kamp 	else if (bp->bio_disk != NULL)
37a9463ba8SPoul-Henning Kamp 		printf("%s%d: %s ",
38a9463ba8SPoul-Henning Kamp 		    bp->bio_disk->d_name, bp->bio_disk->d_unit, what);
39a9463ba8SPoul-Henning Kamp 	else
40a9463ba8SPoul-Henning Kamp 		printf("disk??: %s ", what);
41f90c382cSPoul-Henning Kamp 	switch(bp->bio_cmd) {
42f90c382cSPoul-Henning Kamp 	case BIO_READ:		printf("cmd=read "); break;
43f90c382cSPoul-Henning Kamp 	case BIO_WRITE:		printf("cmd=write "); break;
44f90c382cSPoul-Henning Kamp 	case BIO_DELETE:	printf("cmd=delete "); break;
45f90c382cSPoul-Henning Kamp 	case BIO_GETATTR:	printf("cmd=getattr "); break;
46f90c382cSPoul-Henning Kamp 	default:		printf("cmd=%x ", bp->bio_cmd); break;
47f90c382cSPoul-Henning Kamp 	}
481ad9172fSPoul-Henning Kamp 	sn = bp->bio_pblkno;
49f90c382cSPoul-Henning Kamp 	if (bp->bio_bcount <= DEV_BSIZE) {
50f90c382cSPoul-Henning Kamp 		printf("fsbn %jd%s", (intmax_t)sn, nl ? "\n" : "");
51f90c382cSPoul-Henning Kamp 		return;
52f90c382cSPoul-Henning Kamp 	}
53f90c382cSPoul-Henning Kamp 	if (blkdone >= 0) {
54f90c382cSPoul-Henning Kamp 		sn += blkdone;
55f90c382cSPoul-Henning Kamp 		printf("fsbn %jd of ", (intmax_t)sn);
56f90c382cSPoul-Henning Kamp 	}
571ad9172fSPoul-Henning Kamp 	printf("%jd-%jd", (intmax_t)bp->bio_pblkno,
581ad9172fSPoul-Henning Kamp 	    (intmax_t)(bp->bio_pblkno + (bp->bio_bcount - 1) / DEV_BSIZE));
59f90c382cSPoul-Henning Kamp 	if (nl)
60f90c382cSPoul-Henning Kamp 		printf("\n");
61f90c382cSPoul-Henning Kamp }
622382fb0aSPoul-Henning Kamp 
632382fb0aSPoul-Henning Kamp /*
64d086f85aSPoul-Henning Kamp  * BIO queue implementation
65d086f85aSPoul-Henning Kamp  */
66d086f85aSPoul-Henning Kamp 
67d086f85aSPoul-Henning Kamp void
68d086f85aSPoul-Henning Kamp bioq_init(struct bio_queue_head *head)
69d086f85aSPoul-Henning Kamp {
70d086f85aSPoul-Henning Kamp 	TAILQ_INIT(&head->queue);
714cb4df48SPoul-Henning Kamp 	head->last_offset = 0;
72d086f85aSPoul-Henning Kamp 	head->insert_point = NULL;
73d086f85aSPoul-Henning Kamp 	head->switch_point = NULL;
74d086f85aSPoul-Henning Kamp }
75d086f85aSPoul-Henning Kamp 
76d086f85aSPoul-Henning Kamp void
77d086f85aSPoul-Henning Kamp bioq_remove(struct bio_queue_head *head, struct bio *bp)
78d086f85aSPoul-Henning Kamp {
79d086f85aSPoul-Henning Kamp 	if (bp == head->switch_point)
80d086f85aSPoul-Henning Kamp 		head->switch_point = TAILQ_NEXT(bp, bio_queue);
81d086f85aSPoul-Henning Kamp 	if (bp == head->insert_point) {
82d086f85aSPoul-Henning Kamp 		head->insert_point = TAILQ_PREV(bp, bio_queue, bio_queue);
83d086f85aSPoul-Henning Kamp 		if (head->insert_point == NULL)
844cb4df48SPoul-Henning Kamp 			head->last_offset = 0;
85d086f85aSPoul-Henning Kamp 	} else if (bp == TAILQ_FIRST(&head->queue))
864cb4df48SPoul-Henning Kamp 		head->last_offset = bp->bio_offset;
87d086f85aSPoul-Henning Kamp 	TAILQ_REMOVE(&head->queue, bp, bio_queue);
88d086f85aSPoul-Henning Kamp 	if (TAILQ_FIRST(&head->queue) == head->switch_point)
89d086f85aSPoul-Henning Kamp 		head->switch_point = NULL;
90d086f85aSPoul-Henning Kamp }
91af6ca7f4SPoul-Henning Kamp 
92af6ca7f4SPoul-Henning Kamp void
93af6ca7f4SPoul-Henning Kamp bioq_flush(struct bio_queue_head *head, struct devstat *stp, int error)
94af6ca7f4SPoul-Henning Kamp {
95af6ca7f4SPoul-Henning Kamp 	struct bio *bp;
96af6ca7f4SPoul-Henning Kamp 
97d298f919SPoul-Henning Kamp 	while ((bp = bioq_takefirst(head)) != NULL)
98b8404473SPoul-Henning Kamp 		biofinish(bp, stp, error);
99af6ca7f4SPoul-Henning Kamp }
100af6ca7f4SPoul-Henning Kamp 
101d086f85aSPoul-Henning Kamp void
102bf484316SPawel Jakub Dawidek bioq_insert_head(struct bio_queue_head *head, struct bio *bp)
103bf484316SPawel Jakub Dawidek {
104bf484316SPawel Jakub Dawidek 
105bf484316SPawel Jakub Dawidek 	TAILQ_INSERT_HEAD(&head->queue, bp, bio_queue);
106bf484316SPawel Jakub Dawidek }
107bf484316SPawel Jakub Dawidek 
108bf484316SPawel Jakub Dawidek void
109d086f85aSPoul-Henning Kamp bioq_insert_tail(struct bio_queue_head *head, struct bio *bp)
110d086f85aSPoul-Henning Kamp {
111d086f85aSPoul-Henning Kamp 
112d086f85aSPoul-Henning Kamp 	TAILQ_INSERT_TAIL(&head->queue, bp, bio_queue);
113d086f85aSPoul-Henning Kamp }
114d086f85aSPoul-Henning Kamp 
115d086f85aSPoul-Henning Kamp struct bio *
116d086f85aSPoul-Henning Kamp bioq_first(struct bio_queue_head *head)
117d086f85aSPoul-Henning Kamp {
118d086f85aSPoul-Henning Kamp 
119d086f85aSPoul-Henning Kamp 	return (TAILQ_FIRST(&head->queue));
120d086f85aSPoul-Henning Kamp }
121d086f85aSPoul-Henning Kamp 
122d298f919SPoul-Henning Kamp struct bio *
123d298f919SPoul-Henning Kamp bioq_takefirst(struct bio_queue_head *head)
124d298f919SPoul-Henning Kamp {
125d298f919SPoul-Henning Kamp 	struct bio *bp;
126d298f919SPoul-Henning Kamp 
127d298f919SPoul-Henning Kamp 	bp = TAILQ_FIRST(&head->queue);
128d298f919SPoul-Henning Kamp 	if (bp != NULL)
129d298f919SPoul-Henning Kamp 		bioq_remove(head, bp);
130d298f919SPoul-Henning Kamp 	return (bp);
131d298f919SPoul-Henning Kamp }
132d086f85aSPoul-Henning Kamp 
133d086f85aSPoul-Henning Kamp /*
1342382fb0aSPoul-Henning Kamp  * Seek sort for disks.
1352382fb0aSPoul-Henning Kamp  *
1362382fb0aSPoul-Henning Kamp  * The buf_queue keep two queues, sorted in ascending block order.  The first
1372382fb0aSPoul-Henning Kamp  * queue holds those requests which are positioned after the current block
1382382fb0aSPoul-Henning Kamp  * (in the first request); the second, which starts at queue->switch_point,
1392382fb0aSPoul-Henning Kamp  * holds requests which came in after their block number was passed.  Thus
1402382fb0aSPoul-Henning Kamp  * we implement a one way scan, retracting after reaching the end of the drive
1412382fb0aSPoul-Henning Kamp  * to the first request on the second queue, at which time it becomes the
1422382fb0aSPoul-Henning Kamp  * first queue.
1432382fb0aSPoul-Henning Kamp  *
1442382fb0aSPoul-Henning Kamp  * A one-way scan is natural because of the way UNIX read-ahead blocks are
1452382fb0aSPoul-Henning Kamp  * allocated.
1462382fb0aSPoul-Henning Kamp  */
1472382fb0aSPoul-Henning Kamp 
1482382fb0aSPoul-Henning Kamp void
1492382fb0aSPoul-Henning Kamp bioq_disksort(bioq, bp)
1502382fb0aSPoul-Henning Kamp 	struct bio_queue_head *bioq;
1512382fb0aSPoul-Henning Kamp 	struct bio *bp;
1522382fb0aSPoul-Henning Kamp {
1532382fb0aSPoul-Henning Kamp 	struct bio *bq;
1542382fb0aSPoul-Henning Kamp 	struct bio *bn;
1552382fb0aSPoul-Henning Kamp 	struct bio *be;
1562382fb0aSPoul-Henning Kamp 
1572382fb0aSPoul-Henning Kamp 	be = TAILQ_LAST(&bioq->queue, bio_queue);
1582382fb0aSPoul-Henning Kamp 	/*
1592382fb0aSPoul-Henning Kamp 	 * If the queue is empty or we are an
1602382fb0aSPoul-Henning Kamp 	 * ordered transaction, then it's easy.
1612382fb0aSPoul-Henning Kamp 	 */
1622382fb0aSPoul-Henning Kamp 	if ((bq = bioq_first(bioq)) == NULL) {
1632382fb0aSPoul-Henning Kamp 		bioq_insert_tail(bioq, bp);
1642382fb0aSPoul-Henning Kamp 		return;
1652382fb0aSPoul-Henning Kamp 	} else if (bioq->insert_point != NULL) {
1662382fb0aSPoul-Henning Kamp 
1672382fb0aSPoul-Henning Kamp 		/*
1682382fb0aSPoul-Henning Kamp 		 * A certain portion of the list is
1692382fb0aSPoul-Henning Kamp 		 * "locked" to preserve ordering, so
1702382fb0aSPoul-Henning Kamp 		 * we can only insert after the insert
1712382fb0aSPoul-Henning Kamp 		 * point.
1722382fb0aSPoul-Henning Kamp 		 */
1732382fb0aSPoul-Henning Kamp 		bq = bioq->insert_point;
1742382fb0aSPoul-Henning Kamp 	} else {
1752382fb0aSPoul-Henning Kamp 
1762382fb0aSPoul-Henning Kamp 		/*
1772382fb0aSPoul-Henning Kamp 		 * If we lie before the last removed (currently active)
1782382fb0aSPoul-Henning Kamp 		 * request, and are not inserting ourselves into the
1792382fb0aSPoul-Henning Kamp 		 * "locked" portion of the list, then we must add ourselves
1802382fb0aSPoul-Henning Kamp 		 * to the second request list.
1812382fb0aSPoul-Henning Kamp 		 */
1824cb4df48SPoul-Henning Kamp 		if (bp->bio_offset < bioq->last_offset) {
1832382fb0aSPoul-Henning Kamp 
1842382fb0aSPoul-Henning Kamp 			bq = bioq->switch_point;
1852382fb0aSPoul-Henning Kamp 			/*
1862382fb0aSPoul-Henning Kamp 			 * If we are starting a new secondary list,
1872382fb0aSPoul-Henning Kamp 			 * then it's easy.
1882382fb0aSPoul-Henning Kamp 			 */
1892382fb0aSPoul-Henning Kamp 			if (bq == NULL) {
1902382fb0aSPoul-Henning Kamp 				bioq->switch_point = bp;
1912382fb0aSPoul-Henning Kamp 				bioq_insert_tail(bioq, bp);
1922382fb0aSPoul-Henning Kamp 				return;
1932382fb0aSPoul-Henning Kamp 			}
1942382fb0aSPoul-Henning Kamp 			/*
1952382fb0aSPoul-Henning Kamp 			 * If we lie ahead of the current switch point,
1962382fb0aSPoul-Henning Kamp 			 * insert us before the switch point and move
1972382fb0aSPoul-Henning Kamp 			 * the switch point.
1982382fb0aSPoul-Henning Kamp 			 */
1994cb4df48SPoul-Henning Kamp 			if (bp->bio_offset < bq->bio_offset) {
2002382fb0aSPoul-Henning Kamp 				bioq->switch_point = bp;
2012382fb0aSPoul-Henning Kamp 				TAILQ_INSERT_BEFORE(bq, bp, bio_queue);
2022382fb0aSPoul-Henning Kamp 				return;
2032382fb0aSPoul-Henning Kamp 			}
2042382fb0aSPoul-Henning Kamp 		} else {
2052382fb0aSPoul-Henning Kamp 			if (bioq->switch_point != NULL)
2062382fb0aSPoul-Henning Kamp 				be = TAILQ_PREV(bioq->switch_point,
2072382fb0aSPoul-Henning Kamp 						bio_queue, bio_queue);
2082382fb0aSPoul-Henning Kamp 			/*
2094cb4df48SPoul-Henning Kamp 			 * If we lie between last_offset and bq,
2102382fb0aSPoul-Henning Kamp 			 * insert before bq.
2112382fb0aSPoul-Henning Kamp 			 */
2124cb4df48SPoul-Henning Kamp 			if (bp->bio_offset < bq->bio_offset) {
2132382fb0aSPoul-Henning Kamp 				TAILQ_INSERT_BEFORE(bq, bp, bio_queue);
2142382fb0aSPoul-Henning Kamp 				return;
2152382fb0aSPoul-Henning Kamp 			}
2162382fb0aSPoul-Henning Kamp 		}
2172382fb0aSPoul-Henning Kamp 	}
2182382fb0aSPoul-Henning Kamp 
2192382fb0aSPoul-Henning Kamp 	/*
2202382fb0aSPoul-Henning Kamp 	 * Request is at/after our current position in the list.
2212382fb0aSPoul-Henning Kamp 	 * Optimize for sequential I/O by seeing if we go at the tail.
2222382fb0aSPoul-Henning Kamp 	 */
2234cb4df48SPoul-Henning Kamp 	if (bp->bio_offset > be->bio_offset) {
2242382fb0aSPoul-Henning Kamp 		TAILQ_INSERT_AFTER(&bioq->queue, be, bp, bio_queue);
2252382fb0aSPoul-Henning Kamp 		return;
2262382fb0aSPoul-Henning Kamp 	}
2272382fb0aSPoul-Henning Kamp 
2282382fb0aSPoul-Henning Kamp 	/* Otherwise, insertion sort */
2292382fb0aSPoul-Henning Kamp 	while ((bn = TAILQ_NEXT(bq, bio_queue)) != NULL) {
2302382fb0aSPoul-Henning Kamp 
2312382fb0aSPoul-Henning Kamp 		/*
2322382fb0aSPoul-Henning Kamp 		 * We want to go after the current request if it is the end
2332382fb0aSPoul-Henning Kamp 		 * of the first request list, or if the next request is a
2342382fb0aSPoul-Henning Kamp 		 * larger cylinder than our request.
2352382fb0aSPoul-Henning Kamp 		 */
2362382fb0aSPoul-Henning Kamp 		if (bn == bioq->switch_point
2374cb4df48SPoul-Henning Kamp 		 || bp->bio_offset < bn->bio_offset)
2382382fb0aSPoul-Henning Kamp 			break;
2392382fb0aSPoul-Henning Kamp 		bq = bn;
2402382fb0aSPoul-Henning Kamp 	}
2412382fb0aSPoul-Henning Kamp 	TAILQ_INSERT_AFTER(&bioq->queue, bq, bp, bio_queue);
2422382fb0aSPoul-Henning Kamp }
2432382fb0aSPoul-Henning Kamp 
2442382fb0aSPoul-Henning Kamp 
245