]> git.sesse.net Git - bcachefs-tools-debian/blobdiff - linux/bio.c
Disable pristine-tar option in gbp.conf, since there is no pristine-tar branch.
[bcachefs-tools-debian] / linux / bio.c
index f43566993171bf3c7f0641a1f7a06d996b6230aa..93a791c4bb98a205b149ff888d6883e7eaa3eeb0 100644 (file)
 #include <linux/blkdev.h>
 #include <linux/slab.h>
 #include <linux/kernel.h>
-#include <linux/export.h>
+
+static const struct {
+       int             err;
+       const char      *name;
+} blk_errors[] = {
+       [BLK_STS_OK]            = { 0,          "" },
+       [BLK_STS_NOTSUPP]       = { -EOPNOTSUPP, "operation not supported" },
+       [BLK_STS_TIMEOUT]       = { -ETIMEDOUT, "timeout" },
+       [BLK_STS_NOSPC]         = { -ENOSPC,    "critical space allocation" },
+       [BLK_STS_TRANSPORT]     = { -ENOLINK,   "recoverable transport" },
+       [BLK_STS_TARGET]        = { -EREMOTEIO, "critical target" },
+       [BLK_STS_NEXUS]         = { -EBADE,     "critical nexus" },
+       [BLK_STS_MEDIUM]        = { -ENODATA,   "critical medium" },
+       [BLK_STS_PROTECTION]    = { -EILSEQ,    "protection" },
+       [BLK_STS_RESOURCE]      = { -ENOMEM,    "kernel resource" },
+       [BLK_STS_AGAIN]         = { -EAGAIN,    "nonblocking retry" },
+
+       /* device mapper special case, should not leak out: */
+       [BLK_STS_DM_REQUEUE]    = { -EREMCHG, "dm internal retry" },
+
+       /* everything else not covered above: */
+       [BLK_STS_IOERR]         = { -EIO,       "I/O" },
+};
+
+int blk_status_to_errno(blk_status_t status)
+{
+       int idx = (__force int)status;
+
+       if (WARN_ON_ONCE(idx >= ARRAY_SIZE(blk_errors)))
+               return -EIO;
+       return blk_errors[idx].err;
+}
+
+const char *blk_status_to_str(blk_status_t status)
+{
+       int idx = (__force int)status;
+
+       if (WARN_ON_ONCE(idx >= ARRAY_SIZE(blk_errors)))
+               return "(invalid error)";
+       return blk_errors[idx].name;
+}
 
 void bio_copy_data_iter(struct bio *dst, struct bvec_iter *dst_iter,
                        struct bio *src, struct bvec_iter *src_iter)
@@ -80,29 +120,30 @@ void zero_fill_bio_iter(struct bio *bio, struct bvec_iter start)
        }
 }
 
-void __bio_clone_fast(struct bio *bio, struct bio *bio_src)
+static int __bio_clone(struct bio *bio, struct bio *bio_src, gfp_t gfp)
 {
-       /*
-        * most users will be overriding ->bi_bdev with a new target,
-        * so we don't set nor calculate new physical/hw segment counts here
-        */
-       bio->bi_bdev = bio_src->bi_bdev;
        bio_set_flag(bio, BIO_CLONED);
-       bio->bi_opf = bio_src->bi_opf;
+       bio->bi_ioprio = bio_src->bi_ioprio;
        bio->bi_iter = bio_src->bi_iter;
-       bio->bi_io_vec = bio_src->bi_io_vec;
+       return 0;
 }
 
-struct bio *bio_clone_fast(struct bio *bio, gfp_t gfp_mask, struct bio_set *bs)
+struct bio *bio_alloc_clone(struct block_device *bdev, struct bio *bio_src,
+               gfp_t gfp, struct bio_set *bs)
 {
-       struct bio *b;
+       struct bio *bio;
 
-       b = bio_alloc_bioset(gfp_mask, 0, bs);
-       if (!b)
+       bio = bio_alloc_bioset(bdev, 0, bio_src->bi_opf, gfp, bs);
+       if (!bio)
+               return NULL;
+
+       if (__bio_clone(bio, bio_src, gfp) < 0) {
+               bio_put(bio);
                return NULL;
+       }
+       bio->bi_io_vec = bio_src->bi_io_vec;
 
-       __bio_clone_fast(b, bio);
-       return b;
+       return bio;
 }
 
 struct bio *bio_split(struct bio *bio, int sectors,
@@ -113,15 +154,7 @@ struct bio *bio_split(struct bio *bio, int sectors,
        BUG_ON(sectors <= 0);
        BUG_ON(sectors >= bio_sectors(bio));
 
-       /*
-        * Discards need a mutable bio_vec to accommodate the payload
-        * required by the DSM TRIM and UNMAP commands.
-        */
-       if (bio_op(bio) == REQ_OP_DISCARD || bio_op(bio) == REQ_OP_SECURE_ERASE)
-               split = bio_clone_bioset(bio, gfp, bs);
-       else
-               split = bio_clone_fast(bio, gfp, bs);
-
+       split = bio_alloc_clone(bio->bi_bdev, bio, gfp, bs);
        if (!split)
                return NULL;
 
@@ -132,21 +165,13 @@ struct bio *bio_split(struct bio *bio, int sectors,
        return split;
 }
 
-int bio_alloc_pages(struct bio *bio, gfp_t gfp_mask)
+void bio_free_pages(struct bio *bio)
 {
-       int i;
-       struct bio_vec *bv;
-
-       bio_for_each_segment_all(bv, bio, i) {
-               bv->bv_page = alloc_page(gfp_mask);
-               if (!bv->bv_page) {
-                       while (--bv >= bio->bi_io_vec)
-                               __free_page(bv->bv_page);
-                       return -ENOMEM;
-               }
-       }
+       struct bvec_iter_all iter;
+       struct bio_vec *bvec;
 
-       return 0;
+       bio_for_each_segment_all(bvec, bio, iter)
+               __free_page(bvec->bv_page);
 }
 
 void bio_advance(struct bio *bio, unsigned bytes)
@@ -156,9 +181,16 @@ void bio_advance(struct bio *bio, unsigned bytes)
 
 static void bio_free(struct bio *bio)
 {
-       unsigned front_pad = bio->bi_pool ? bio->bi_pool->front_pad : 0;
+       struct bio_set *bs = bio->bi_pool;
+
+       if (bs) {
+               if (bio->bi_max_vecs > BIO_INLINE_VECS)
+                       mempool_free(bio->bi_io_vec, &bs->bvec_pool);
 
-       kfree((void *) bio - front_pad);
+               mempool_free((void *) bio - bs->front_pad, &bs->bio_pool);
+       } else {
+               kfree(bio);
+       }
 }
 
 void bio_put(struct bio *bio)
@@ -176,6 +208,23 @@ void bio_put(struct bio *bio)
        }
 }
 
+int bio_add_page(struct bio *bio, struct page *page,
+                unsigned int len, unsigned int off)
+{
+       struct bio_vec *bv = &bio->bi_io_vec[bio->bi_vcnt];
+
+       WARN_ON_ONCE(bio_flagged(bio, BIO_CLONED));
+       WARN_ON_ONCE(bio->bi_vcnt >= bio->bi_max_vecs);
+
+       bv->bv_page = page;
+       bv->bv_offset = off;
+       bv->bv_len = len;
+
+       bio->bi_iter.bi_size += len;
+       bio->bi_vcnt++;
+       return len;
+}
+
 static inline bool bio_remaining_done(struct bio *bio)
 {
        /*
@@ -199,8 +248,8 @@ static struct bio *__bio_chain_endio(struct bio *bio)
 {
        struct bio *parent = bio->bi_private;
 
-       if (!parent->bi_error)
-               parent->bi_error = bio->bi_error;
+       if (!parent->bi_status)
+               parent->bi_status = bio->bi_status;
        bio_put(bio);
        return parent;
 }
@@ -233,85 +282,114 @@ again:
                bio->bi_end_io(bio);
 }
 
-void bio_endio_nodec(struct bio *bio)
-{
-       goto nodec;
-
-       while (bio) {
-               if (unlikely(!bio_remaining_done(bio)))
-                       break;
-nodec:
-               if (bio->bi_end_io == bio_chain_endio) {
-                       struct bio *parent = bio->bi_private;
-                       parent->bi_error = bio->bi_error;
-                       bio_put(bio);
-                       bio = parent;
-               } else {
-                       if (bio->bi_end_io)
-                               bio->bi_end_io(bio);
-                       bio = NULL;
-               }
-       }
-}
-
-void bio_reset(struct bio *bio)
+void bio_reset(struct bio *bio, struct block_device *bdev, unsigned int opf)
 {
        unsigned long flags = bio->bi_flags & (~0UL << BIO_RESET_BITS);
 
        memset(bio, 0, BIO_RESET_BYTES);
-       bio->bi_flags = flags;
+       bio->bi_bdev    = bdev;
+       bio->bi_opf     = opf;
+       bio->bi_flags   = flags;
        atomic_set(&bio->__bi_remaining, 1);
 }
 
-struct bio *bio_alloc_bioset(gfp_t gfp_mask, int nr_iovecs, struct bio_set *bs)
+struct bio *bio_kmalloc(unsigned int nr_iovecs, gfp_t gfp_mask)
 {
-       unsigned front_pad = bs ? bs->front_pad : 0;
        struct bio *bio;
-       void *p;
-
-       p = kmalloc(front_pad +
-                   sizeof(struct bio) +
-                   nr_iovecs * sizeof(struct bio_vec),
-                   gfp_mask);
 
-       if (unlikely(!p))
+       bio = kmalloc(sizeof(struct bio) +
+                     sizeof(struct bio_vec) * nr_iovecs, gfp_mask);
+       if (unlikely(!bio))
                return NULL;
+       bio_init(bio, NULL, nr_iovecs ? bio->bi_inline_vecs : NULL, nr_iovecs, 0);
+       bio->bi_pool = NULL;
+       return bio;
+}
 
-       bio = p + front_pad;
-       bio_init(bio, bio->bi_inline_vecs, nr_iovecs);
-       bio->bi_pool = bs;
+static struct bio_vec *bvec_alloc(mempool_t *pool, int *nr_vecs,
+               gfp_t gfp_mask)
+{
+       *nr_vecs = roundup_pow_of_two(*nr_vecs);
+       /*
+        * Try a slab allocation first for all smaller allocations.  If that
+        * fails and __GFP_DIRECT_RECLAIM is set retry with the mempool.
+        * The mempool is sized to handle up to BIO_MAX_VECS entries.
+        */
+       if (*nr_vecs < BIO_MAX_VECS) {
+               struct bio_vec *bvl;
 
-       return bio;
+               bvl = kmalloc(sizeof(*bvl) * *nr_vecs, gfp_mask);
+               if (likely(bvl))
+                       return bvl;
+               *nr_vecs = BIO_MAX_VECS;
+       }
+
+       return mempool_alloc(pool, gfp_mask);
 }
 
-struct bio *bio_clone_bioset(struct bio *bio_src, gfp_t gfp_mask,
+struct bio *bio_alloc_bioset(struct block_device *bdev,
+                            unsigned nr_iovecs,
+                            unsigned opf,
+                            gfp_t gfp_mask,
                             struct bio_set *bs)
 {
-       struct bvec_iter iter;
-       struct bio_vec bv;
        struct bio *bio;
+       void *p;
 
-       bio = bio_alloc_bioset(gfp_mask, bio_segments(bio_src), bs);
-       if (!bio)
+       if (nr_iovecs > BIO_MAX_VECS)
+               return NULL;
+
+       p = mempool_alloc(&bs->bio_pool, gfp_mask);
+       if (unlikely(!p))
                return NULL;
 
-       bio->bi_bdev            = bio_src->bi_bdev;
-       bio->bi_opf             = bio_src->bi_opf;
-       bio->bi_iter.bi_sector  = bio_src->bi_iter.bi_sector;
-       bio->bi_iter.bi_size    = bio_src->bi_iter.bi_size;
-
-       switch (bio_op(bio)) {
-       case REQ_OP_DISCARD:
-       case REQ_OP_SECURE_ERASE:
-               break;
-       case REQ_OP_WRITE_SAME:
-               bio->bi_io_vec[bio->bi_vcnt++] = bio_src->bi_io_vec[0];
-               break;
-       default:
-               bio_for_each_segment(bv, bio_src, iter)
-                       bio->bi_io_vec[bio->bi_vcnt++] = bv;
-               break;
+       bio = p + bs->front_pad;
+       if (nr_iovecs > BIO_INLINE_VECS) {
+               struct bio_vec *bvl = NULL;
+
+               bvl = bvec_alloc(&bs->bvec_pool, &nr_iovecs, gfp_mask);
+               if (unlikely(!bvl))
+                       goto err_free;
+
+               bio_init(bio, bdev, bvl, nr_iovecs, opf);
+       } else if (nr_iovecs) {
+               bio_init(bio, bdev, bio->bi_inline_vecs, BIO_INLINE_VECS, opf);
+       } else {
+               bio_init(bio, bdev, NULL, 0, opf);
        }
 
+       bio->bi_pool = bs;
        return bio;
+
+err_free:
+       mempool_free(p, &bs->bio_pool);
+       return NULL;
+}
+
+void bioset_exit(struct bio_set *bs)
+{
+       mempool_exit(&bs->bio_pool);
+       mempool_exit(&bs->bvec_pool);
+}
+
+int bioset_init(struct bio_set *bs,
+               unsigned int pool_size,
+               unsigned int front_pad,
+               int flags)
+{
+       int ret;
+
+       bs->front_pad = front_pad;
+       if (flags & BIOSET_NEED_BVECS)
+               bs->back_pad = BIO_INLINE_VECS * sizeof(struct bio_vec);
+       else
+               bs->back_pad = 0;
+
+       ret   = mempool_init_kmalloc_pool(&bs->bio_pool, pool_size, bs->front_pad +
+                                         sizeof(struct bio) + bs->back_pad) ?:
+               mempool_init_kmalloc_pool(&bs->bvec_pool, pool_size,
+                                         sizeof(struct bio_vec) * BIO_MAX_VECS);
+       if (ret)
+               bioset_exit(bs);
+       return ret;
 }