]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/kern/subr_disk.c
- Move softdep from using a global worklist to per-mount worklists. This
[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 }
74
75 void
76 bioq_remove(struct bio_queue_head *head, struct bio *bp)
77 {
78         if (bp == head->insert_point) {
79                 head->last_offset = bp->bio_offset;
80                 head->insert_point = TAILQ_NEXT(bp, bio_queue);
81                 if (head->insert_point == NULL) {
82                         head->last_offset = 0;
83                         head->insert_point = TAILQ_FIRST(&head->queue);
84                 }
85         }
86         TAILQ_REMOVE(&head->queue, bp, bio_queue);
87 }
88
89 void
90 bioq_flush(struct bio_queue_head *head, struct devstat *stp, int error)
91 {
92         struct bio *bp;
93
94         while ((bp = bioq_takefirst(head)) != NULL)
95                 biofinish(bp, stp, error);
96 }
97
98 void
99 bioq_insert_head(struct bio_queue_head *head, struct bio *bp)
100 {
101
102         if (TAILQ_FIRST(&head->queue) == NULL)
103                 head->insert_point = bp;
104         TAILQ_INSERT_HEAD(&head->queue, bp, bio_queue);
105 }
106
107 void
108 bioq_insert_tail(struct bio_queue_head *head, struct bio *bp)
109 {
110
111         if (TAILQ_FIRST(&head->queue) == NULL)
112                 head->insert_point = bp;
113         TAILQ_INSERT_TAIL(&head->queue, bp, bio_queue);
114 }
115
116 struct bio *
117 bioq_first(struct bio_queue_head *head)
118 {
119
120         return (TAILQ_FIRST(&head->queue));
121 }
122
123 struct bio *
124 bioq_takefirst(struct bio_queue_head *head)
125 {
126         struct bio *bp;
127
128         bp = TAILQ_FIRST(&head->queue);
129         if (bp != NULL)
130                 bioq_remove(head, bp);
131         return (bp);
132 }
133
134 /*
135  * Seek sort for disks.
136  *
137  * The disksort algorithm sorts all requests in a single queue while keeping
138  * track of the current position of the disk with insert_point and
139  * last_offset.  last_offset is the offset of the last block sent to disk, or
140  * 0 once we reach the end.  insert_point points to the first buf after
141  * last_offset, and is used to slightly speed up insertions.  Blocks are
142  * always sorted in ascending order and the queue always restarts at 0.
143  * This implements the one-way scan which optimizes disk seek times.
144  */
145 void
146 bioq_disksort(bioq, bp)
147         struct bio_queue_head *bioq;
148         struct bio *bp;
149 {
150         struct bio *bq;
151         struct bio *bn;
152
153         /*
154          * If the queue is empty then it's easy.
155          */
156         if (bioq_first(bioq) == NULL) {
157                 bioq_insert_tail(bioq, bp);
158                 return;
159         }
160         /*
161          * Optimize for sequential I/O by seeing if we go at the tail.
162          */
163         bq = TAILQ_LAST(&bioq->queue, bio_queue);
164         if (bp->bio_offset > bq->bio_offset) {
165                 TAILQ_INSERT_AFTER(&bioq->queue, bq, bp, bio_queue);
166                 return;
167         }
168         /*
169          * Pick our scan start based on the last request.  A poor man's
170          * binary search.
171          */
172         if (bp->bio_offset >= bioq->last_offset) { 
173                 bq = bioq->insert_point;
174                 /*
175                  * If we're before the next bio and after the last offset,
176                  * update insert_point;
177                  */
178                 if (bp->bio_offset < bq->bio_offset) {
179                         bioq->insert_point = bp;
180                         TAILQ_INSERT_BEFORE(bq, bp, bio_queue);
181                         return;
182                 }
183         } else
184                 bq = TAILQ_FIRST(&bioq->queue);
185         if (bp->bio_offset < bq->bio_offset) {
186                 TAILQ_INSERT_BEFORE(bq, bp, bio_queue);
187                 return;
188         }
189         /* Insertion sort */
190         while ((bn = TAILQ_NEXT(bq, bio_queue)) != NULL) {
191                 if (bp->bio_offset < bn->bio_offset)
192                         break;
193                 bq = bn;
194         }
195         TAILQ_INSERT_AFTER(&bioq->queue, bq, bp, bio_queue);
196 }