xref: /freebsd/sys/kern/subr_disk.c (revision 6780ab54325a71e7e70112b11657973edde8655e)
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$
10  *
11  */
12 
13 #include "opt_geom.h"
14 
15 #include <sys/param.h>
16 #include <sys/systm.h>
17 #include <sys/stdint.h>
18 #include <sys/bio.h>
19 #include <sys/conf.h>
20 #include <sys/disk.h>
21 #include <sys/disklabel.h>
22 
23 /*-
24  * Disk error is the preface to plaintive error messages
25  * about failing disk transfers.  It prints messages of the form
26  * 	"hp0g: BLABLABLA cmd=read fsbn 12345 of 12344-12347"
27  * blkdone should be -1 if the position of the error is unknown.
28  * The message is printed with printf.
29  */
30 void
31 disk_err(struct bio *bp, const char *what, int blkdone, int nl)
32 {
33 	daddr_t sn;
34 
35 	printf("%s: %s ", devtoname(bp->bio_dev), what);
36 	switch(bp->bio_cmd) {
37 	case BIO_READ:		printf("cmd=read "); break;
38 	case BIO_WRITE:		printf("cmd=write "); break;
39 	case BIO_DELETE:	printf("cmd=delete "); break;
40 	case BIO_GETATTR:	printf("cmd=getattr "); break;
41 	case BIO_SETATTR:	printf("cmd=setattr "); break;
42 	default:		printf("cmd=%x ", bp->bio_cmd); break;
43 	}
44 	sn = bp->bio_blkno;
45 	if (bp->bio_bcount <= DEV_BSIZE) {
46 		printf("fsbn %jd%s", (intmax_t)sn, nl ? "\n" : "");
47 		return;
48 	}
49 	if (blkdone >= 0) {
50 		sn += blkdone;
51 		printf("fsbn %jd of ", (intmax_t)sn);
52 	}
53 	printf("%jd-%jd", (intmax_t)bp->bio_blkno,
54 	    (intmax_t)(bp->bio_blkno + (bp->bio_bcount - 1) / DEV_BSIZE));
55 	if (nl)
56 		printf("\n");
57 }
58 
59 /*
60  * Seek sort for disks.
61  *
62  * The buf_queue keep two queues, sorted in ascending block order.  The first
63  * queue holds those requests which are positioned after the current block
64  * (in the first request); the second, which starts at queue->switch_point,
65  * holds requests which came in after their block number was passed.  Thus
66  * we implement a one way scan, retracting after reaching the end of the drive
67  * to the first request on the second queue, at which time it becomes the
68  * first queue.
69  *
70  * A one-way scan is natural because of the way UNIX read-ahead blocks are
71  * allocated.
72  */
73 
74 void
75 bioq_disksort(bioq, bp)
76 	struct bio_queue_head *bioq;
77 	struct bio *bp;
78 {
79 	struct bio *bq;
80 	struct bio *bn;
81 	struct bio *be;
82 
83 	if (!atomic_cmpset_int(&bioq->busy, 0, 1))
84 		panic("Recursing in bioq_disksort()");
85 	be = TAILQ_LAST(&bioq->queue, bio_queue);
86 	/*
87 	 * If the queue is empty or we are an
88 	 * ordered transaction, then it's easy.
89 	 */
90 	if ((bq = bioq_first(bioq)) == NULL) {
91 		bioq_insert_tail(bioq, bp);
92 		bioq->busy = 0;
93 		return;
94 	} else if (bioq->insert_point != NULL) {
95 
96 		/*
97 		 * A certain portion of the list is
98 		 * "locked" to preserve ordering, so
99 		 * we can only insert after the insert
100 		 * point.
101 		 */
102 		bq = bioq->insert_point;
103 	} else {
104 
105 		/*
106 		 * If we lie before the last removed (currently active)
107 		 * request, and are not inserting ourselves into the
108 		 * "locked" portion of the list, then we must add ourselves
109 		 * to the second request list.
110 		 */
111 		if (bp->bio_pblkno < bioq->last_pblkno) {
112 
113 			bq = bioq->switch_point;
114 			/*
115 			 * If we are starting a new secondary list,
116 			 * then it's easy.
117 			 */
118 			if (bq == NULL) {
119 				bioq->switch_point = bp;
120 				bioq_insert_tail(bioq, bp);
121 				bioq->busy = 0;
122 				return;
123 			}
124 			/*
125 			 * If we lie ahead of the current switch point,
126 			 * insert us before the switch point and move
127 			 * the switch point.
128 			 */
129 			if (bp->bio_pblkno < bq->bio_pblkno) {
130 				bioq->switch_point = bp;
131 				TAILQ_INSERT_BEFORE(bq, bp, bio_queue);
132 				bioq->busy = 0;
133 				return;
134 			}
135 		} else {
136 			if (bioq->switch_point != NULL)
137 				be = TAILQ_PREV(bioq->switch_point,
138 						bio_queue, bio_queue);
139 			/*
140 			 * If we lie between last_pblkno and bq,
141 			 * insert before bq.
142 			 */
143 			if (bp->bio_pblkno < bq->bio_pblkno) {
144 				TAILQ_INSERT_BEFORE(bq, bp, bio_queue);
145 				bioq->busy = 0;
146 				return;
147 			}
148 		}
149 	}
150 
151 	/*
152 	 * Request is at/after our current position in the list.
153 	 * Optimize for sequential I/O by seeing if we go at the tail.
154 	 */
155 	if (bp->bio_pblkno > be->bio_pblkno) {
156 		TAILQ_INSERT_AFTER(&bioq->queue, be, bp, bio_queue);
157 		bioq->busy = 0;
158 		return;
159 	}
160 
161 	/* Otherwise, insertion sort */
162 	while ((bn = TAILQ_NEXT(bq, bio_queue)) != NULL) {
163 
164 		/*
165 		 * We want to go after the current request if it is the end
166 		 * of the first request list, or if the next request is a
167 		 * larger cylinder than our request.
168 		 */
169 		if (bn == bioq->switch_point
170 		 || bp->bio_pblkno < bn->bio_pblkno)
171 			break;
172 		bq = bn;
173 	}
174 	TAILQ_INSERT_AFTER(&bioq->queue, bq, bp, bio_queue);
175 	bioq->busy = 0;
176 }
177 
178 
179