1831058deSDavid Howells /* bounce buffer handling for block devices 2831058deSDavid Howells * 3831058deSDavid Howells * - Split from highmem.c 4831058deSDavid Howells */ 5831058deSDavid Howells 6*b1de0d13SMitchel Humpherys #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 7*b1de0d13SMitchel Humpherys 8831058deSDavid Howells #include <linux/mm.h> 9b95f1b31SPaul Gortmaker #include <linux/export.h> 10831058deSDavid Howells #include <linux/swap.h> 115a0e3ad6STejun Heo #include <linux/gfp.h> 12831058deSDavid Howells #include <linux/bio.h> 13831058deSDavid Howells #include <linux/pagemap.h> 14831058deSDavid Howells #include <linux/mempool.h> 15831058deSDavid Howells #include <linux/blkdev.h> 16831058deSDavid Howells #include <linux/init.h> 17831058deSDavid Howells #include <linux/hash.h> 18831058deSDavid Howells #include <linux/highmem.h> 193bcfeaf9SDavid Vrabel #include <linux/bootmem.h> 20*b1de0d13SMitchel Humpherys #include <linux/printk.h> 21831058deSDavid Howells #include <asm/tlbflush.h> 22831058deSDavid Howells 2355782138SLi Zefan #include <trace/events/block.h> 2455782138SLi Zefan 25831058deSDavid Howells #define POOL_SIZE 64 26831058deSDavid Howells #define ISA_POOL_SIZE 16 27831058deSDavid Howells 28831058deSDavid Howells static mempool_t *page_pool, *isa_page_pool; 29831058deSDavid Howells 30f1006257SChris Metcalf #if defined(CONFIG_HIGHMEM) || defined(CONFIG_NEED_BOUNCE_POOL) 31831058deSDavid Howells static __init int init_emergency_pool(void) 32831058deSDavid Howells { 33f1006257SChris Metcalf #if defined(CONFIG_HIGHMEM) && !defined(CONFIG_MEMORY_HOTPLUG) 343bcfeaf9SDavid Vrabel if (max_pfn <= max_low_pfn) 35831058deSDavid Howells return 0; 363bcfeaf9SDavid Vrabel #endif 37831058deSDavid Howells 38831058deSDavid Howells page_pool = mempool_create_page_pool(POOL_SIZE, 0); 39831058deSDavid Howells BUG_ON(!page_pool); 40*b1de0d13SMitchel Humpherys pr_info("pool size: %d pages\n", POOL_SIZE); 41831058deSDavid Howells 42831058deSDavid Howells return 0; 43831058deSDavid Howells } 44831058deSDavid Howells 45831058deSDavid Howells __initcall(init_emergency_pool); 46f1006257SChris Metcalf #endif 47831058deSDavid Howells 48f1006257SChris Metcalf #ifdef CONFIG_HIGHMEM 49831058deSDavid Howells /* 50831058deSDavid Howells * highmem version, map in to vec 51831058deSDavid Howells */ 52831058deSDavid Howells static void bounce_copy_vec(struct bio_vec *to, unsigned char *vfrom) 53831058deSDavid Howells { 54831058deSDavid Howells unsigned long flags; 55831058deSDavid Howells unsigned char *vto; 56831058deSDavid Howells 57831058deSDavid Howells local_irq_save(flags); 589b04c5feSCong Wang vto = kmap_atomic(to->bv_page); 59831058deSDavid Howells memcpy(vto + to->bv_offset, vfrom, to->bv_len); 609b04c5feSCong Wang kunmap_atomic(vto); 61831058deSDavid Howells local_irq_restore(flags); 62831058deSDavid Howells } 63831058deSDavid Howells 64831058deSDavid Howells #else /* CONFIG_HIGHMEM */ 65831058deSDavid Howells 66831058deSDavid Howells #define bounce_copy_vec(to, vfrom) \ 67831058deSDavid Howells memcpy(page_address((to)->bv_page) + (to)->bv_offset, vfrom, (to)->bv_len) 68831058deSDavid Howells 69831058deSDavid Howells #endif /* CONFIG_HIGHMEM */ 70831058deSDavid Howells 71831058deSDavid Howells /* 72831058deSDavid Howells * allocate pages in the DMA region for the ISA pool 73831058deSDavid Howells */ 74831058deSDavid Howells static void *mempool_alloc_pages_isa(gfp_t gfp_mask, void *data) 75831058deSDavid Howells { 76831058deSDavid Howells return mempool_alloc_pages(gfp_mask | GFP_DMA, data); 77831058deSDavid Howells } 78831058deSDavid Howells 79831058deSDavid Howells /* 80831058deSDavid Howells * gets called "every" time someone init's a queue with BLK_BOUNCE_ISA 81831058deSDavid Howells * as the max address, so check if the pool has already been created. 82831058deSDavid Howells */ 83831058deSDavid Howells int init_emergency_isa_pool(void) 84831058deSDavid Howells { 85831058deSDavid Howells if (isa_page_pool) 86831058deSDavid Howells return 0; 87831058deSDavid Howells 88831058deSDavid Howells isa_page_pool = mempool_create(ISA_POOL_SIZE, mempool_alloc_pages_isa, 89831058deSDavid Howells mempool_free_pages, (void *) 0); 90831058deSDavid Howells BUG_ON(!isa_page_pool); 91831058deSDavid Howells 92*b1de0d13SMitchel Humpherys pr_info("isa pool size: %d pages\n", ISA_POOL_SIZE); 93831058deSDavid Howells return 0; 94831058deSDavid Howells } 95831058deSDavid Howells 96831058deSDavid Howells /* 97831058deSDavid Howells * Simple bounce buffer support for highmem pages. Depending on the 98831058deSDavid Howells * queue gfp mask set, *to may or may not be a highmem page. kmap it 99831058deSDavid Howells * always, it will do the Right Thing 100831058deSDavid Howells */ 101831058deSDavid Howells static void copy_to_high_bio_irq(struct bio *to, struct bio *from) 102831058deSDavid Howells { 103831058deSDavid Howells unsigned char *vfrom; 1047988613bSKent Overstreet struct bio_vec tovec, *fromvec = from->bi_io_vec; 1057988613bSKent Overstreet struct bvec_iter iter; 106831058deSDavid Howells 1077988613bSKent Overstreet bio_for_each_segment(tovec, to, iter) { 1087988613bSKent Overstreet if (tovec.bv_page != fromvec->bv_page) { 109831058deSDavid Howells /* 1107988613bSKent Overstreet * fromvec->bv_offset and fromvec->bv_len might have 1117988613bSKent Overstreet * been modified by the block layer, so use the original 1127988613bSKent Overstreet * copy, bounce_copy_vec already uses tovec->bv_len 113831058deSDavid Howells */ 1147988613bSKent Overstreet vfrom = page_address(fromvec->bv_page) + 1157988613bSKent Overstreet tovec.bv_offset; 116831058deSDavid Howells 1177988613bSKent Overstreet bounce_copy_vec(&tovec, vfrom); 1187988613bSKent Overstreet flush_dcache_page(tovec.bv_page); 1197988613bSKent Overstreet } 120831058deSDavid Howells 1217988613bSKent Overstreet fromvec++; 122831058deSDavid Howells } 123831058deSDavid Howells } 124831058deSDavid Howells 125831058deSDavid Howells static void bounce_end_io(struct bio *bio, mempool_t *pool, int err) 126831058deSDavid Howells { 127831058deSDavid Howells struct bio *bio_orig = bio->bi_private; 128831058deSDavid Howells struct bio_vec *bvec, *org_vec; 129831058deSDavid Howells int i; 130831058deSDavid Howells 131831058deSDavid Howells if (test_bit(BIO_EOPNOTSUPP, &bio->bi_flags)) 132831058deSDavid Howells set_bit(BIO_EOPNOTSUPP, &bio_orig->bi_flags); 133831058deSDavid Howells 134831058deSDavid Howells /* 135831058deSDavid Howells * free up bounce indirect pages used 136831058deSDavid Howells */ 137d74c6d51SKent Overstreet bio_for_each_segment_all(bvec, bio, i) { 138831058deSDavid Howells org_vec = bio_orig->bi_io_vec + i; 139831058deSDavid Howells if (bvec->bv_page == org_vec->bv_page) 140831058deSDavid Howells continue; 141831058deSDavid Howells 142831058deSDavid Howells dec_zone_page_state(bvec->bv_page, NR_BOUNCE); 143831058deSDavid Howells mempool_free(bvec->bv_page, pool); 144831058deSDavid Howells } 145831058deSDavid Howells 1466712ecf8SNeilBrown bio_endio(bio_orig, err); 147831058deSDavid Howells bio_put(bio); 148831058deSDavid Howells } 149831058deSDavid Howells 1506712ecf8SNeilBrown static void bounce_end_io_write(struct bio *bio, int err) 151831058deSDavid Howells { 152831058deSDavid Howells bounce_end_io(bio, page_pool, err); 153831058deSDavid Howells } 154831058deSDavid Howells 1556712ecf8SNeilBrown static void bounce_end_io_write_isa(struct bio *bio, int err) 156831058deSDavid Howells { 157831058deSDavid Howells 158831058deSDavid Howells bounce_end_io(bio, isa_page_pool, err); 159831058deSDavid Howells } 160831058deSDavid Howells 161831058deSDavid Howells static void __bounce_end_io_read(struct bio *bio, mempool_t *pool, int err) 162831058deSDavid Howells { 163831058deSDavid Howells struct bio *bio_orig = bio->bi_private; 164831058deSDavid Howells 165831058deSDavid Howells if (test_bit(BIO_UPTODATE, &bio->bi_flags)) 166831058deSDavid Howells copy_to_high_bio_irq(bio_orig, bio); 167831058deSDavid Howells 168831058deSDavid Howells bounce_end_io(bio, pool, err); 169831058deSDavid Howells } 170831058deSDavid Howells 1716712ecf8SNeilBrown static void bounce_end_io_read(struct bio *bio, int err) 172831058deSDavid Howells { 173831058deSDavid Howells __bounce_end_io_read(bio, page_pool, err); 174831058deSDavid Howells } 175831058deSDavid Howells 1766712ecf8SNeilBrown static void bounce_end_io_read_isa(struct bio *bio, int err) 177831058deSDavid Howells { 178831058deSDavid Howells __bounce_end_io_read(bio, isa_page_pool, err); 179831058deSDavid Howells } 180831058deSDavid Howells 181ffecfd1aSDarrick J. Wong #ifdef CONFIG_NEED_BOUNCE_POOL 182ffecfd1aSDarrick J. Wong static int must_snapshot_stable_pages(struct request_queue *q, struct bio *bio) 183ffecfd1aSDarrick J. Wong { 184ffecfd1aSDarrick J. Wong if (bio_data_dir(bio) != WRITE) 185ffecfd1aSDarrick J. Wong return 0; 186ffecfd1aSDarrick J. Wong 187ffecfd1aSDarrick J. Wong if (!bdi_cap_stable_pages_required(&q->backing_dev_info)) 188ffecfd1aSDarrick J. Wong return 0; 189ffecfd1aSDarrick J. Wong 19071368511SDarrick J. Wong return test_bit(BIO_SNAP_STABLE, &bio->bi_flags); 191ffecfd1aSDarrick J. Wong } 192ffecfd1aSDarrick J. Wong #else 193ffecfd1aSDarrick J. Wong static int must_snapshot_stable_pages(struct request_queue *q, struct bio *bio) 194ffecfd1aSDarrick J. Wong { 195ffecfd1aSDarrick J. Wong return 0; 196ffecfd1aSDarrick J. Wong } 197ffecfd1aSDarrick J. Wong #endif /* CONFIG_NEED_BOUNCE_POOL */ 198ffecfd1aSDarrick J. Wong 199165125e1SJens Axboe static void __blk_queue_bounce(struct request_queue *q, struct bio **bio_orig, 200ffecfd1aSDarrick J. Wong mempool_t *pool, int force) 201831058deSDavid Howells { 2026bc454d1SKent Overstreet struct bio *bio; 2036bc454d1SKent Overstreet int rw = bio_data_dir(*bio_orig); 2047988613bSKent Overstreet struct bio_vec *to, from; 2057988613bSKent Overstreet struct bvec_iter iter; 2066bc454d1SKent Overstreet unsigned i; 207831058deSDavid Howells 20883b2944fSDarrick J. Wong if (force) 20983b2944fSDarrick J. Wong goto bounce; 2107988613bSKent Overstreet bio_for_each_segment(from, *bio_orig, iter) 2117988613bSKent Overstreet if (page_to_pfn(from.bv_page) > queue_bounce_pfn(q)) 2126bc454d1SKent Overstreet goto bounce; 213831058deSDavid Howells 2146bc454d1SKent Overstreet return; 2156bc454d1SKent Overstreet bounce: 2166bc454d1SKent Overstreet bio = bio_clone_bioset(*bio_orig, GFP_NOIO, fs_bio_set); 2176bc454d1SKent Overstreet 218cb34e057SKent Overstreet bio_for_each_segment_all(to, bio, i) { 2196bc454d1SKent Overstreet struct page *page = to->bv_page; 2206bc454d1SKent Overstreet 221ffecfd1aSDarrick J. Wong if (page_to_pfn(page) <= queue_bounce_pfn(q) && !force) 222831058deSDavid Howells continue; 223831058deSDavid Howells 224831058deSDavid Howells inc_zone_page_state(to->bv_page, NR_BOUNCE); 2256bc454d1SKent Overstreet to->bv_page = mempool_alloc(pool, q->bounce_gfp); 226831058deSDavid Howells 227831058deSDavid Howells if (rw == WRITE) { 228831058deSDavid Howells char *vto, *vfrom; 229831058deSDavid Howells 2306bc454d1SKent Overstreet flush_dcache_page(page); 231831058deSDavid Howells 2326bc454d1SKent Overstreet vto = page_address(to->bv_page) + to->bv_offset; 2336bc454d1SKent Overstreet vfrom = kmap_atomic(page) + to->bv_offset; 2346bc454d1SKent Overstreet memcpy(vto, vfrom, to->bv_len); 2356bc454d1SKent Overstreet kunmap_atomic(vfrom); 2366bc454d1SKent Overstreet } 2376bc454d1SKent Overstreet } 238831058deSDavid Howells 2395f3ea37cSArnaldo Carvalho de Melo trace_block_bio_bounce(q, *bio_orig); 240c43a5082SJens Axboe 241831058deSDavid Howells bio->bi_flags |= (1 << BIO_BOUNCED); 242831058deSDavid Howells 243831058deSDavid Howells if (pool == page_pool) { 244831058deSDavid Howells bio->bi_end_io = bounce_end_io_write; 245831058deSDavid Howells if (rw == READ) 246831058deSDavid Howells bio->bi_end_io = bounce_end_io_read; 247831058deSDavid Howells } else { 248831058deSDavid Howells bio->bi_end_io = bounce_end_io_write_isa; 249831058deSDavid Howells if (rw == READ) 250831058deSDavid Howells bio->bi_end_io = bounce_end_io_read_isa; 251831058deSDavid Howells } 252831058deSDavid Howells 253831058deSDavid Howells bio->bi_private = *bio_orig; 254831058deSDavid Howells *bio_orig = bio; 255831058deSDavid Howells } 256831058deSDavid Howells 257165125e1SJens Axboe void blk_queue_bounce(struct request_queue *q, struct bio **bio_orig) 258831058deSDavid Howells { 259ffecfd1aSDarrick J. Wong int must_bounce; 260831058deSDavid Howells mempool_t *pool; 261831058deSDavid Howells 262831058deSDavid Howells /* 263bf2de6f5SJens Axboe * Data-less bio, nothing to bounce 264bf2de6f5SJens Axboe */ 26536144077SJens Axboe if (!bio_has_data(*bio_orig)) 266bf2de6f5SJens Axboe return; 267bf2de6f5SJens Axboe 268ffecfd1aSDarrick J. Wong must_bounce = must_snapshot_stable_pages(q, *bio_orig); 269ffecfd1aSDarrick J. Wong 270bf2de6f5SJens Axboe /* 271831058deSDavid Howells * for non-isa bounce case, just check if the bounce pfn is equal 272831058deSDavid Howells * to or bigger than the highest pfn in the system -- in that case, 273831058deSDavid Howells * don't waste time iterating over bio segments 274831058deSDavid Howells */ 275831058deSDavid Howells if (!(q->bounce_gfp & GFP_DMA)) { 276ffecfd1aSDarrick J. Wong if (queue_bounce_pfn(q) >= blk_max_pfn && !must_bounce) 277831058deSDavid Howells return; 278831058deSDavid Howells pool = page_pool; 279831058deSDavid Howells } else { 280831058deSDavid Howells BUG_ON(!isa_page_pool); 281831058deSDavid Howells pool = isa_page_pool; 282831058deSDavid Howells } 283831058deSDavid Howells 284831058deSDavid Howells /* 285831058deSDavid Howells * slow path 286831058deSDavid Howells */ 287ffecfd1aSDarrick J. Wong __blk_queue_bounce(q, bio_orig, pool, must_bounce); 288831058deSDavid Howells } 289831058deSDavid Howells 290831058deSDavid Howells EXPORT_SYMBOL(blk_queue_bounce); 291