19454b2d8SWarner Losh /*- 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 * ---------------------------------------------------------------------------- 8d4619572SLuigi Rizzo * 9d4619572SLuigi Rizzo * The bioq_disksort() (and the specification of the bioq API) 10d4619572SLuigi Rizzo * have been written by Luigi Rizzo and Fabio Checconi under the same 11d4619572SLuigi Rizzo * license as above. 12da9e4f55SPoul-Henning Kamp */ 13da9e4f55SPoul-Henning Kamp 14677b542eSDavid E. O'Brien #include <sys/cdefs.h> 15677b542eSDavid E. O'Brien __FBSDID("$FreeBSD$"); 16677b542eSDavid E. O'Brien 17417fb7f6SPoul-Henning Kamp #include "opt_geom.h" 18417fb7f6SPoul-Henning Kamp 19da9e4f55SPoul-Henning Kamp #include <sys/param.h> 20da9e4f55SPoul-Henning Kamp #include <sys/systm.h> 219626b608SPoul-Henning Kamp #include <sys/bio.h> 22da9e4f55SPoul-Henning Kamp #include <sys/conf.h> 23da9e4f55SPoul-Henning Kamp #include <sys/disk.h> 2481750927SPoul-Henning Kamp #include <geom/geom_disk.h> 25f90c382cSPoul-Henning Kamp 26*1a996ed1SEdward Tomasz Napierala /*- 27f90c382cSPoul-Henning Kamp * Disk error is the preface to plaintive error messages 28f90c382cSPoul-Henning Kamp * about failing disk transfers. It prints messages of the form 29f90c382cSPoul-Henning Kamp * "hp0g: BLABLABLA cmd=read fsbn 12345 of 12344-12347" 30f90c382cSPoul-Henning Kamp * blkdone should be -1 if the position of the error is unknown. 31f90c382cSPoul-Henning Kamp * The message is printed with printf. 32f90c382cSPoul-Henning Kamp */ 33f90c382cSPoul-Henning Kamp void 34f90c382cSPoul-Henning Kamp disk_err(struct bio *bp, const char *what, int blkdone, int nl) 35f90c382cSPoul-Henning Kamp { 36f90c382cSPoul-Henning Kamp daddr_t sn; 37f90c382cSPoul-Henning Kamp 38a9463ba8SPoul-Henning Kamp if (bp->bio_dev != NULL) 39f90c382cSPoul-Henning Kamp printf("%s: %s ", devtoname(bp->bio_dev), what); 40a9463ba8SPoul-Henning Kamp else if (bp->bio_disk != NULL) 41a9463ba8SPoul-Henning Kamp printf("%s%d: %s ", 42a9463ba8SPoul-Henning Kamp bp->bio_disk->d_name, bp->bio_disk->d_unit, what); 43a9463ba8SPoul-Henning Kamp else 44a9463ba8SPoul-Henning Kamp printf("disk??: %s ", what); 45f90c382cSPoul-Henning Kamp switch(bp->bio_cmd) { 46f90c382cSPoul-Henning Kamp case BIO_READ: printf("cmd=read "); break; 47f90c382cSPoul-Henning Kamp case BIO_WRITE: printf("cmd=write "); break; 48f90c382cSPoul-Henning Kamp case BIO_DELETE: printf("cmd=delete "); break; 49f90c382cSPoul-Henning Kamp case BIO_GETATTR: printf("cmd=getattr "); break; 50c3618c65SPawel Jakub Dawidek case BIO_FLUSH: printf("cmd=flush "); break; 51f90c382cSPoul-Henning Kamp default: printf("cmd=%x ", bp->bio_cmd); break; 52f90c382cSPoul-Henning Kamp } 531ad9172fSPoul-Henning Kamp sn = bp->bio_pblkno; 54f90c382cSPoul-Henning Kamp if (bp->bio_bcount <= DEV_BSIZE) { 55f90c382cSPoul-Henning Kamp printf("fsbn %jd%s", (intmax_t)sn, nl ? "\n" : ""); 56f90c382cSPoul-Henning Kamp return; 57f90c382cSPoul-Henning Kamp } 58f90c382cSPoul-Henning Kamp if (blkdone >= 0) { 59f90c382cSPoul-Henning Kamp sn += blkdone; 60f90c382cSPoul-Henning Kamp printf("fsbn %jd of ", (intmax_t)sn); 61f90c382cSPoul-Henning Kamp } 621ad9172fSPoul-Henning Kamp printf("%jd-%jd", (intmax_t)bp->bio_pblkno, 631ad9172fSPoul-Henning Kamp (intmax_t)(bp->bio_pblkno + (bp->bio_bcount - 1) / DEV_BSIZE)); 64f90c382cSPoul-Henning Kamp if (nl) 65f90c382cSPoul-Henning Kamp printf("\n"); 66f90c382cSPoul-Henning Kamp } 672382fb0aSPoul-Henning Kamp 682382fb0aSPoul-Henning Kamp /* 69d086f85aSPoul-Henning Kamp * BIO queue implementation 70d4619572SLuigi Rizzo * 71d4619572SLuigi Rizzo * Please read carefully the description below before making any change 72d4619572SLuigi Rizzo * to the code, or you might change the behaviour of the data structure 73d4619572SLuigi Rizzo * in undesirable ways. 74d4619572SLuigi Rizzo * 75d4619572SLuigi Rizzo * A bioq stores disk I/O request (bio), normally sorted according to 76d4619572SLuigi Rizzo * the distance of the requested position (bio->bio_offset) from the 77d4619572SLuigi Rizzo * current head position (bioq->last_offset) in the scan direction, i.e. 78d4619572SLuigi Rizzo * 79d4619572SLuigi Rizzo * (uoff_t)(bio_offset - last_offset) 80d4619572SLuigi Rizzo * 81d4619572SLuigi Rizzo * Note that the cast to unsigned (uoff_t) is fundamental to insure 82d4619572SLuigi Rizzo * that the distance is computed in the scan direction. 83d4619572SLuigi Rizzo * 84d4619572SLuigi Rizzo * The main methods for manipulating the bioq are: 85d4619572SLuigi Rizzo * 86d4619572SLuigi Rizzo * bioq_disksort() performs an ordered insertion; 87d4619572SLuigi Rizzo * 88d4619572SLuigi Rizzo * bioq_first() return the head of the queue, without removing; 89d4619572SLuigi Rizzo * 90d4619572SLuigi Rizzo * bioq_takefirst() return and remove the head of the queue, 91d4619572SLuigi Rizzo * updating the 'current head position' as 92d4619572SLuigi Rizzo * bioq->last_offset = bio->bio_offset + bio->bio_length; 93d4619572SLuigi Rizzo * 94d4619572SLuigi Rizzo * When updating the 'current head position', we assume that the result of 95d4619572SLuigi Rizzo * bioq_takefirst() is dispatched to the device, so bioq->last_offset 96d4619572SLuigi Rizzo * represents the head position once the request is complete. 97d4619572SLuigi Rizzo * 98d4619572SLuigi Rizzo * If the bioq is manipulated using only the above calls, it starts 99d4619572SLuigi Rizzo * with a sorted sequence of requests with bio_offset >= last_offset, 100d4619572SLuigi Rizzo * possibly followed by another sorted sequence of requests with 101d4619572SLuigi Rizzo * 0 <= bio_offset < bioq->last_offset 102d4619572SLuigi Rizzo * 103d4619572SLuigi Rizzo * NOTE: historical behaviour was to ignore bio->bio_length in the 104d4619572SLuigi Rizzo * update, but its use tracks the head position in a better way. 105d4619572SLuigi Rizzo * Historical behaviour was also to update the head position when 106d4619572SLuigi Rizzo * the request under service is complete, rather than when the 107d4619572SLuigi Rizzo * request is extracted from the queue. However, the current API 108d4619572SLuigi Rizzo * has no method to update the head position; secondly, once 109d4619572SLuigi Rizzo * a request has been submitted to the disk, we have no idea of 110d4619572SLuigi Rizzo * the actual head position, so the final one is our best guess. 111d4619572SLuigi Rizzo * 112d4619572SLuigi Rizzo * --- Direct queue manipulation --- 113d4619572SLuigi Rizzo * 114d4619572SLuigi Rizzo * A bioq uses an underlying TAILQ to store requests, so we also 115d4619572SLuigi Rizzo * export methods to manipulate the TAILQ, in particular: 116d4619572SLuigi Rizzo * 117d4619572SLuigi Rizzo * bioq_insert_tail() insert an entry at the end. 118d4619572SLuigi Rizzo * It also creates a 'barrier' so all subsequent 119d4619572SLuigi Rizzo * insertions through bioq_disksort() will end up 120d4619572SLuigi Rizzo * after this entry; 121d4619572SLuigi Rizzo * 122d4619572SLuigi Rizzo * bioq_insert_head() insert an entry at the head, update 123d4619572SLuigi Rizzo * bioq->last_offset = bio->bio_offset so that 124d4619572SLuigi Rizzo * all subsequent insertions through bioq_disksort() 125d4619572SLuigi Rizzo * will end up after this entry; 126d4619572SLuigi Rizzo * 127d4619572SLuigi Rizzo * bioq_remove() remove a generic element from the queue, act as 128d4619572SLuigi Rizzo * bioq_takefirst() if invoked on the head of the queue. 129d4619572SLuigi Rizzo * 130d4619572SLuigi Rizzo * The semantic of these methods is the same of the operations 131d4619572SLuigi Rizzo * on the underlying TAILQ, but with additional guarantees on 132d4619572SLuigi Rizzo * subsequent bioq_disksort() calls. E.g. bioq_insert_tail() 133d4619572SLuigi Rizzo * can be useful for making sure that all previous ops are flushed 134d4619572SLuigi Rizzo * to disk before continuing. 135d4619572SLuigi Rizzo * 136d4619572SLuigi Rizzo * Updating bioq->last_offset on a bioq_insert_head() guarantees 137d4619572SLuigi Rizzo * that the bio inserted with the last bioq_insert_head() will stay 138d4619572SLuigi Rizzo * at the head of the queue even after subsequent bioq_disksort(). 139d4619572SLuigi Rizzo * 140d4619572SLuigi Rizzo * Note that when the direct queue manipulation functions are used, 141d4619572SLuigi Rizzo * the queue may contain multiple inversion points (i.e. more than 142d4619572SLuigi Rizzo * two sorted sequences of requests). 143d4619572SLuigi Rizzo * 144d086f85aSPoul-Henning Kamp */ 145d086f85aSPoul-Henning Kamp 146d086f85aSPoul-Henning Kamp void 147d086f85aSPoul-Henning Kamp bioq_init(struct bio_queue_head *head) 148d086f85aSPoul-Henning Kamp { 149d4619572SLuigi Rizzo 150d086f85aSPoul-Henning Kamp TAILQ_INIT(&head->queue); 1514cb4df48SPoul-Henning Kamp head->last_offset = 0; 152d086f85aSPoul-Henning Kamp head->insert_point = NULL; 153d086f85aSPoul-Henning Kamp } 154d086f85aSPoul-Henning Kamp 155d086f85aSPoul-Henning Kamp void 156d086f85aSPoul-Henning Kamp bioq_remove(struct bio_queue_head *head, struct bio *bp) 157d086f85aSPoul-Henning Kamp { 158d4619572SLuigi Rizzo 159d4619572SLuigi Rizzo if (bp == TAILQ_FIRST(&head->queue)) 160d4619572SLuigi Rizzo head->last_offset = bp->bio_offset + bp->bio_length; 161d4619572SLuigi Rizzo 162d4619572SLuigi Rizzo if (bp == head->insert_point) 163d4619572SLuigi Rizzo head->insert_point = NULL; 164d4619572SLuigi Rizzo 165d086f85aSPoul-Henning Kamp TAILQ_REMOVE(&head->queue, bp, bio_queue); 166d086f85aSPoul-Henning Kamp } 167af6ca7f4SPoul-Henning Kamp 168af6ca7f4SPoul-Henning Kamp void 169af6ca7f4SPoul-Henning Kamp bioq_flush(struct bio_queue_head *head, struct devstat *stp, int error) 170af6ca7f4SPoul-Henning Kamp { 171af6ca7f4SPoul-Henning Kamp struct bio *bp; 172af6ca7f4SPoul-Henning Kamp 173d298f919SPoul-Henning Kamp while ((bp = bioq_takefirst(head)) != NULL) 174b8404473SPoul-Henning Kamp biofinish(bp, stp, error); 175af6ca7f4SPoul-Henning Kamp } 176af6ca7f4SPoul-Henning Kamp 177d086f85aSPoul-Henning Kamp void 178bf484316SPawel Jakub Dawidek bioq_insert_head(struct bio_queue_head *head, struct bio *bp) 179bf484316SPawel Jakub Dawidek { 180bf484316SPawel Jakub Dawidek 181d4619572SLuigi Rizzo head->last_offset = bp->bio_offset; 182bf484316SPawel Jakub Dawidek TAILQ_INSERT_HEAD(&head->queue, bp, bio_queue); 183bf484316SPawel Jakub Dawidek } 184bf484316SPawel Jakub Dawidek 185bf484316SPawel Jakub Dawidek void 186d086f85aSPoul-Henning Kamp bioq_insert_tail(struct bio_queue_head *head, struct bio *bp) 187d086f85aSPoul-Henning Kamp { 188d086f85aSPoul-Henning Kamp 189d086f85aSPoul-Henning Kamp TAILQ_INSERT_TAIL(&head->queue, bp, bio_queue); 190d4619572SLuigi Rizzo head->insert_point = bp; 191d086f85aSPoul-Henning Kamp } 192d086f85aSPoul-Henning Kamp 193d086f85aSPoul-Henning Kamp struct bio * 194d086f85aSPoul-Henning Kamp bioq_first(struct bio_queue_head *head) 195d086f85aSPoul-Henning Kamp { 196d086f85aSPoul-Henning Kamp 197d086f85aSPoul-Henning Kamp return (TAILQ_FIRST(&head->queue)); 198d086f85aSPoul-Henning Kamp } 199d086f85aSPoul-Henning Kamp 200d298f919SPoul-Henning Kamp struct bio * 201d298f919SPoul-Henning Kamp bioq_takefirst(struct bio_queue_head *head) 202d298f919SPoul-Henning Kamp { 203d298f919SPoul-Henning Kamp struct bio *bp; 204d298f919SPoul-Henning Kamp 205d298f919SPoul-Henning Kamp bp = TAILQ_FIRST(&head->queue); 206d298f919SPoul-Henning Kamp if (bp != NULL) 207d298f919SPoul-Henning Kamp bioq_remove(head, bp); 208d298f919SPoul-Henning Kamp return (bp); 209d298f919SPoul-Henning Kamp } 210d086f85aSPoul-Henning Kamp 211d086f85aSPoul-Henning Kamp /* 212d4619572SLuigi Rizzo * Compute the sorting key. The cast to unsigned is 213d4619572SLuigi Rizzo * fundamental for correctness, see the description 214d4619572SLuigi Rizzo * near the beginning of the file. 2152382fb0aSPoul-Henning Kamp */ 216d4619572SLuigi Rizzo static inline uoff_t 217d4619572SLuigi Rizzo bioq_bio_key(struct bio_queue_head *head, struct bio *bp) 2182382fb0aSPoul-Henning Kamp { 219d4619572SLuigi Rizzo 220d4619572SLuigi Rizzo return ((uoff_t)(bp->bio_offset - head->last_offset)); 221d4619572SLuigi Rizzo } 2222382fb0aSPoul-Henning Kamp 2232382fb0aSPoul-Henning Kamp /* 224d4619572SLuigi Rizzo * Seek sort for disks. 225d4619572SLuigi Rizzo * 226d4619572SLuigi Rizzo * Sort all requests in a single queue while keeping 227d4619572SLuigi Rizzo * track of the current position of the disk with last_offset. 228d4619572SLuigi Rizzo * See above for details. 2292382fb0aSPoul-Henning Kamp */ 230d4619572SLuigi Rizzo void 231d4619572SLuigi Rizzo bioq_disksort(struct bio_queue_head *head, struct bio *bp) 232d4619572SLuigi Rizzo { 233d4619572SLuigi Rizzo struct bio *cur, *prev = NULL; 234d4619572SLuigi Rizzo uoff_t key = bioq_bio_key(head, bp); 235d4619572SLuigi Rizzo 236d4619572SLuigi Rizzo cur = TAILQ_FIRST(&head->queue); 237d4619572SLuigi Rizzo 238d4619572SLuigi Rizzo if (head->insert_point) 239d4619572SLuigi Rizzo cur = head->insert_point; 240d4619572SLuigi Rizzo 241d4619572SLuigi Rizzo while (cur != NULL && key >= bioq_bio_key(head, cur)) { 242d4619572SLuigi Rizzo prev = cur; 243d4619572SLuigi Rizzo cur = TAILQ_NEXT(cur, bio_queue); 2442382fb0aSPoul-Henning Kamp } 245d4619572SLuigi Rizzo 246d4619572SLuigi Rizzo if (prev == NULL) 247d4619572SLuigi Rizzo TAILQ_INSERT_HEAD(&head->queue, bp, bio_queue); 248d4619572SLuigi Rizzo else 249d4619572SLuigi Rizzo TAILQ_INSERT_AFTER(&head->queue, prev, bp, bio_queue); 2502382fb0aSPoul-Henning Kamp } 251