diff options
author | Jens Axboe <axboe@fb.com> | 2014-05-19 22:01:52 -0400 |
---|---|---|
committer | Jens Axboe <axboe@fb.com> | 2014-05-19 22:01:52 -0400 |
commit | 719c555f4424b194905aa3512a754c0444f27ce8 (patch) | |
tree | 6afd4d62345f1fac7046e23d8907ac555846eb5a /block | |
parent | 39a9f97e5ea99e048c4980c23cf197f6e77995cb (diff) |
block: move mm/bounce.c to block/
Continue moving some of the block files that are scattered around.
bounce.c contains only code for bouncing the contents of a bio.
It's block proper code, not mm code.
Suggested-by: Ming Lei <tom.leiming@gmail.com>
Signed-off-by: Jens Axboe <axboe@fb.com>
Diffstat (limited to 'block')
-rw-r--r-- | block/Makefile | 1 | ||||
-rw-r--r-- | block/bounce.c | 287 |
2 files changed, 288 insertions, 0 deletions
diff --git a/block/Makefile b/block/Makefile index b4c4d3b99a2d..a2ce6ac935ec 100644 --- a/block/Makefile +++ b/block/Makefile | |||
@@ -10,6 +10,7 @@ obj-$(CONFIG_BLOCK) := bio.o elevator.o blk-core.o blk-tag.o blk-sysfs.o \ | |||
10 | genhd.o scsi_ioctl.o partition-generic.o ioprio.o \ | 10 | genhd.o scsi_ioctl.o partition-generic.o ioprio.o \ |
11 | partitions/ | 11 | partitions/ |
12 | 12 | ||
13 | obj-$(CONFIG_BOUNCE) += bounce.o | ||
13 | obj-$(CONFIG_BLK_DEV_BSG) += bsg.o | 14 | obj-$(CONFIG_BLK_DEV_BSG) += bsg.o |
14 | obj-$(CONFIG_BLK_DEV_BSGLIB) += bsg-lib.o | 15 | obj-$(CONFIG_BLK_DEV_BSGLIB) += bsg-lib.o |
15 | obj-$(CONFIG_BLK_CGROUP) += blk-cgroup.o | 16 | obj-$(CONFIG_BLK_CGROUP) += blk-cgroup.o |
diff --git a/block/bounce.c b/block/bounce.c new file mode 100644 index 000000000000..523918b8c6dc --- /dev/null +++ b/block/bounce.c | |||
@@ -0,0 +1,287 @@ | |||
1 | /* bounce buffer handling for block devices | ||
2 | * | ||
3 | * - Split from highmem.c | ||
4 | */ | ||
5 | |||
6 | #include <linux/mm.h> | ||
7 | #include <linux/export.h> | ||
8 | #include <linux/swap.h> | ||
9 | #include <linux/gfp.h> | ||
10 | #include <linux/bio.h> | ||
11 | #include <linux/pagemap.h> | ||
12 | #include <linux/mempool.h> | ||
13 | #include <linux/blkdev.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <linux/hash.h> | ||
16 | #include <linux/highmem.h> | ||
17 | #include <linux/bootmem.h> | ||
18 | #include <asm/tlbflush.h> | ||
19 | |||
20 | #include <trace/events/block.h> | ||
21 | |||
22 | #define POOL_SIZE 64 | ||
23 | #define ISA_POOL_SIZE 16 | ||
24 | |||
25 | static mempool_t *page_pool, *isa_page_pool; | ||
26 | |||
27 | #if defined(CONFIG_HIGHMEM) || defined(CONFIG_NEED_BOUNCE_POOL) | ||
28 | static __init int init_emergency_pool(void) | ||
29 | { | ||
30 | #if defined(CONFIG_HIGHMEM) && !defined(CONFIG_MEMORY_HOTPLUG) | ||
31 | if (max_pfn <= max_low_pfn) | ||
32 | return 0; | ||
33 | #endif | ||
34 | |||
35 | page_pool = mempool_create_page_pool(POOL_SIZE, 0); | ||
36 | BUG_ON(!page_pool); | ||
37 | printk("bounce pool size: %d pages\n", POOL_SIZE); | ||
38 | |||
39 | return 0; | ||
40 | } | ||
41 | |||
42 | __initcall(init_emergency_pool); | ||
43 | #endif | ||
44 | |||
45 | #ifdef CONFIG_HIGHMEM | ||
46 | /* | ||
47 | * highmem version, map in to vec | ||
48 | */ | ||
49 | static void bounce_copy_vec(struct bio_vec *to, unsigned char *vfrom) | ||
50 | { | ||
51 | unsigned long flags; | ||
52 | unsigned char *vto; | ||
53 | |||
54 | local_irq_save(flags); | ||
55 | vto = kmap_atomic(to->bv_page); | ||
56 | memcpy(vto + to->bv_offset, vfrom, to->bv_len); | ||
57 | kunmap_atomic(vto); | ||
58 | local_irq_restore(flags); | ||
59 | } | ||
60 | |||
61 | #else /* CONFIG_HIGHMEM */ | ||
62 | |||
63 | #define bounce_copy_vec(to, vfrom) \ | ||
64 | memcpy(page_address((to)->bv_page) + (to)->bv_offset, vfrom, (to)->bv_len) | ||
65 | |||
66 | #endif /* CONFIG_HIGHMEM */ | ||
67 | |||
68 | /* | ||
69 | * allocate pages in the DMA region for the ISA pool | ||
70 | */ | ||
71 | static void *mempool_alloc_pages_isa(gfp_t gfp_mask, void *data) | ||
72 | { | ||
73 | return mempool_alloc_pages(gfp_mask | GFP_DMA, data); | ||
74 | } | ||
75 | |||
76 | /* | ||
77 | * gets called "every" time someone init's a queue with BLK_BOUNCE_ISA | ||
78 | * as the max address, so check if the pool has already been created. | ||
79 | */ | ||
80 | int init_emergency_isa_pool(void) | ||
81 | { | ||
82 | if (isa_page_pool) | ||
83 | return 0; | ||
84 | |||
85 | isa_page_pool = mempool_create(ISA_POOL_SIZE, mempool_alloc_pages_isa, | ||
86 | mempool_free_pages, (void *) 0); | ||
87 | BUG_ON(!isa_page_pool); | ||
88 | |||
89 | printk("isa bounce pool size: %d pages\n", ISA_POOL_SIZE); | ||
90 | return 0; | ||
91 | } | ||
92 | |||
93 | /* | ||
94 | * Simple bounce buffer support for highmem pages. Depending on the | ||
95 | * queue gfp mask set, *to may or may not be a highmem page. kmap it | ||
96 | * always, it will do the Right Thing | ||
97 | */ | ||
98 | static void copy_to_high_bio_irq(struct bio *to, struct bio *from) | ||
99 | { | ||
100 | unsigned char *vfrom; | ||
101 | struct bio_vec tovec, *fromvec = from->bi_io_vec; | ||
102 | struct bvec_iter iter; | ||
103 | |||
104 | bio_for_each_segment(tovec, to, iter) { | ||
105 | if (tovec.bv_page != fromvec->bv_page) { | ||
106 | /* | ||
107 | * fromvec->bv_offset and fromvec->bv_len might have | ||
108 | * been modified by the block layer, so use the original | ||
109 | * copy, bounce_copy_vec already uses tovec->bv_len | ||
110 | */ | ||
111 | vfrom = page_address(fromvec->bv_page) + | ||
112 | tovec.bv_offset; | ||
113 | |||
114 | bounce_copy_vec(&tovec, vfrom); | ||
115 | flush_dcache_page(tovec.bv_page); | ||
116 | } | ||
117 | |||
118 | fromvec++; | ||
119 | } | ||
120 | } | ||
121 | |||
122 | static void bounce_end_io(struct bio *bio, mempool_t *pool, int err) | ||
123 | { | ||
124 | struct bio *bio_orig = bio->bi_private; | ||
125 | struct bio_vec *bvec, *org_vec; | ||
126 | int i; | ||
127 | |||
128 | if (test_bit(BIO_EOPNOTSUPP, &bio->bi_flags)) | ||
129 | set_bit(BIO_EOPNOTSUPP, &bio_orig->bi_flags); | ||
130 | |||
131 | /* | ||
132 | * free up bounce indirect pages used | ||
133 | */ | ||
134 | bio_for_each_segment_all(bvec, bio, i) { | ||
135 | org_vec = bio_orig->bi_io_vec + i; | ||
136 | if (bvec->bv_page == org_vec->bv_page) | ||
137 | continue; | ||
138 | |||
139 | dec_zone_page_state(bvec->bv_page, NR_BOUNCE); | ||
140 | mempool_free(bvec->bv_page, pool); | ||
141 | } | ||
142 | |||
143 | bio_endio(bio_orig, err); | ||
144 | bio_put(bio); | ||
145 | } | ||
146 | |||
147 | static void bounce_end_io_write(struct bio *bio, int err) | ||
148 | { | ||
149 | bounce_end_io(bio, page_pool, err); | ||
150 | } | ||
151 | |||
152 | static void bounce_end_io_write_isa(struct bio *bio, int err) | ||
153 | { | ||
154 | |||
155 | bounce_end_io(bio, isa_page_pool, err); | ||
156 | } | ||
157 | |||
158 | static void __bounce_end_io_read(struct bio *bio, mempool_t *pool, int err) | ||
159 | { | ||
160 | struct bio *bio_orig = bio->bi_private; | ||
161 | |||
162 | if (test_bit(BIO_UPTODATE, &bio->bi_flags)) | ||
163 | copy_to_high_bio_irq(bio_orig, bio); | ||
164 | |||
165 | bounce_end_io(bio, pool, err); | ||
166 | } | ||
167 | |||
168 | static void bounce_end_io_read(struct bio *bio, int err) | ||
169 | { | ||
170 | __bounce_end_io_read(bio, page_pool, err); | ||
171 | } | ||
172 | |||
173 | static void bounce_end_io_read_isa(struct bio *bio, int err) | ||
174 | { | ||
175 | __bounce_end_io_read(bio, isa_page_pool, err); | ||
176 | } | ||
177 | |||
178 | #ifdef CONFIG_NEED_BOUNCE_POOL | ||
179 | static int must_snapshot_stable_pages(struct request_queue *q, struct bio *bio) | ||
180 | { | ||
181 | if (bio_data_dir(bio) != WRITE) | ||
182 | return 0; | ||
183 | |||
184 | if (!bdi_cap_stable_pages_required(&q->backing_dev_info)) | ||
185 | return 0; | ||
186 | |||
187 | return test_bit(BIO_SNAP_STABLE, &bio->bi_flags); | ||
188 | } | ||
189 | #else | ||
190 | static int must_snapshot_stable_pages(struct request_queue *q, struct bio *bio) | ||
191 | { | ||
192 | return 0; | ||
193 | } | ||
194 | #endif /* CONFIG_NEED_BOUNCE_POOL */ | ||
195 | |||
196 | static void __blk_queue_bounce(struct request_queue *q, struct bio **bio_orig, | ||
197 | mempool_t *pool, int force) | ||
198 | { | ||
199 | struct bio *bio; | ||
200 | int rw = bio_data_dir(*bio_orig); | ||
201 | struct bio_vec *to, from; | ||
202 | struct bvec_iter iter; | ||
203 | unsigned i; | ||
204 | |||
205 | if (force) | ||
206 | goto bounce; | ||
207 | bio_for_each_segment(from, *bio_orig, iter) | ||
208 | if (page_to_pfn(from.bv_page) > queue_bounce_pfn(q)) | ||
209 | goto bounce; | ||
210 | |||
211 | return; | ||
212 | bounce: | ||
213 | bio = bio_clone_bioset(*bio_orig, GFP_NOIO, fs_bio_set); | ||
214 | |||
215 | bio_for_each_segment_all(to, bio, i) { | ||
216 | struct page *page = to->bv_page; | ||
217 | |||
218 | if (page_to_pfn(page) <= queue_bounce_pfn(q) && !force) | ||
219 | continue; | ||
220 | |||
221 | inc_zone_page_state(to->bv_page, NR_BOUNCE); | ||
222 | to->bv_page = mempool_alloc(pool, q->bounce_gfp); | ||
223 | |||
224 | if (rw == WRITE) { | ||
225 | char *vto, *vfrom; | ||
226 | |||
227 | flush_dcache_page(page); | ||
228 | |||
229 | vto = page_address(to->bv_page) + to->bv_offset; | ||
230 | vfrom = kmap_atomic(page) + to->bv_offset; | ||
231 | memcpy(vto, vfrom, to->bv_len); | ||
232 | kunmap_atomic(vfrom); | ||
233 | } | ||
234 | } | ||
235 | |||
236 | trace_block_bio_bounce(q, *bio_orig); | ||
237 | |||
238 | bio->bi_flags |= (1 << BIO_BOUNCED); | ||
239 | |||
240 | if (pool == page_pool) { | ||
241 | bio->bi_end_io = bounce_end_io_write; | ||
242 | if (rw == READ) | ||
243 | bio->bi_end_io = bounce_end_io_read; | ||
244 | } else { | ||
245 | bio->bi_end_io = bounce_end_io_write_isa; | ||
246 | if (rw == READ) | ||
247 | bio->bi_end_io = bounce_end_io_read_isa; | ||
248 | } | ||
249 | |||
250 | bio->bi_private = *bio_orig; | ||
251 | *bio_orig = bio; | ||
252 | } | ||
253 | |||
254 | void blk_queue_bounce(struct request_queue *q, struct bio **bio_orig) | ||
255 | { | ||
256 | int must_bounce; | ||
257 | mempool_t *pool; | ||
258 | |||
259 | /* | ||
260 | * Data-less bio, nothing to bounce | ||
261 | */ | ||
262 | if (!bio_has_data(*bio_orig)) | ||
263 | return; | ||
264 | |||
265 | must_bounce = must_snapshot_stable_pages(q, *bio_orig); | ||
266 | |||
267 | /* | ||
268 | * for non-isa bounce case, just check if the bounce pfn is equal | ||
269 | * to or bigger than the highest pfn in the system -- in that case, | ||
270 | * don't waste time iterating over bio segments | ||
271 | */ | ||
272 | if (!(q->bounce_gfp & GFP_DMA)) { | ||
273 | if (queue_bounce_pfn(q) >= blk_max_pfn && !must_bounce) | ||
274 | return; | ||
275 | pool = page_pool; | ||
276 | } else { | ||
277 | BUG_ON(!isa_page_pool); | ||
278 | pool = isa_page_pool; | ||
279 | } | ||
280 | |||
281 | /* | ||
282 | * slow path | ||
283 | */ | ||
284 | __blk_queue_bounce(q, bio_orig, pool, must_bounce); | ||
285 | } | ||
286 | |||
287 | EXPORT_SYMBOL(blk_queue_bounce); | ||