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