forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
96 lines
2.2 KiB
96 lines
2.2 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
/* |
|
* Copyright (c) 2019 Christoph Hellwig. |
|
*/ |
|
#include "xfs.h" |
|
|
|
static inline unsigned int bio_max_vecs(unsigned int count) |
|
{ |
|
return bio_max_segs(howmany(count, PAGE_SIZE)); |
|
} |
|
|
|
static void |
|
xfs_flush_bdev_async_endio( |
|
struct bio *bio) |
|
{ |
|
complete(bio->bi_private); |
|
} |
|
|
|
/* |
|
* Submit a request for an async cache flush to run. If the request queue does |
|
* not require flush operations, just skip it altogether. If the caller needs |
|
* to wait for the flush completion at a later point in time, they must supply a |
|
* valid completion. This will be signalled when the flush completes. The |
|
* caller never sees the bio that is issued here. |
|
*/ |
|
void |
|
xfs_flush_bdev_async( |
|
struct bio *bio, |
|
struct block_device *bdev, |
|
struct completion *done) |
|
{ |
|
struct request_queue *q = bdev->bd_disk->queue; |
|
|
|
if (!test_bit(QUEUE_FLAG_WC, &q->queue_flags)) { |
|
complete(done); |
|
return; |
|
} |
|
|
|
bio_init(bio, NULL, 0); |
|
bio_set_dev(bio, bdev); |
|
bio->bi_opf = REQ_OP_WRITE | REQ_PREFLUSH | REQ_SYNC; |
|
bio->bi_private = done; |
|
bio->bi_end_io = xfs_flush_bdev_async_endio; |
|
|
|
submit_bio(bio); |
|
} |
|
int |
|
xfs_rw_bdev( |
|
struct block_device *bdev, |
|
sector_t sector, |
|
unsigned int count, |
|
char *data, |
|
unsigned int op) |
|
|
|
{ |
|
unsigned int is_vmalloc = is_vmalloc_addr(data); |
|
unsigned int left = count; |
|
int error; |
|
struct bio *bio; |
|
|
|
if (is_vmalloc && op == REQ_OP_WRITE) |
|
flush_kernel_vmap_range(data, count); |
|
|
|
bio = bio_alloc(GFP_KERNEL, bio_max_vecs(left)); |
|
bio_set_dev(bio, bdev); |
|
bio->bi_iter.bi_sector = sector; |
|
bio->bi_opf = op | REQ_META | REQ_SYNC; |
|
|
|
do { |
|
struct page *page = kmem_to_page(data); |
|
unsigned int off = offset_in_page(data); |
|
unsigned int len = min_t(unsigned, left, PAGE_SIZE - off); |
|
|
|
while (bio_add_page(bio, page, len, off) != len) { |
|
struct bio *prev = bio; |
|
|
|
bio = bio_alloc(GFP_KERNEL, bio_max_vecs(left)); |
|
bio_copy_dev(bio, prev); |
|
bio->bi_iter.bi_sector = bio_end_sector(prev); |
|
bio->bi_opf = prev->bi_opf; |
|
bio_chain(prev, bio); |
|
|
|
submit_bio(prev); |
|
} |
|
|
|
data += len; |
|
left -= len; |
|
} while (left > 0); |
|
|
|
error = submit_bio_wait(bio); |
|
bio_put(bio); |
|
|
|
if (is_vmalloc && op == REQ_OP_READ) |
|
invalidate_kernel_vmap_range(data, count); |
|
return error; |
|
}
|
|
|