mirror of
https://github.com/FEX-Emu/linux.git
synced 2024-12-21 00:42:16 +00:00
b7c44ed9d2
Some places use helpers now, others don't. We only have the 'is set' helper, add helpers for setting and clearing flags too. It was a bit of a mess of atomic vs non-atomic access. With BIO_UPTODATE gone, we don't have any risk of concurrent access to the flags. So relax the restriction and don't make any of them atomic. The flags that do have serialization issues (reffed and chained), we already handle those separately. Signed-off-by: Jens Axboe <axboe@fb.com>
221 lines
5.2 KiB
C
221 lines
5.2 KiB
C
/*
|
|
* Functions related to mapping data to requests
|
|
*/
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/bio.h>
|
|
#include <linux/blkdev.h>
|
|
#include <linux/uio.h>
|
|
|
|
#include "blk.h"
|
|
|
|
int blk_rq_append_bio(struct request_queue *q, struct request *rq,
|
|
struct bio *bio)
|
|
{
|
|
if (!rq->bio)
|
|
blk_rq_bio_prep(q, rq, bio);
|
|
else if (!ll_back_merge_fn(q, rq, bio))
|
|
return -EINVAL;
|
|
else {
|
|
rq->biotail->bi_next = bio;
|
|
rq->biotail = bio;
|
|
|
|
rq->__data_len += bio->bi_iter.bi_size;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int __blk_rq_unmap_user(struct bio *bio)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (bio) {
|
|
if (bio_flagged(bio, BIO_USER_MAPPED))
|
|
bio_unmap_user(bio);
|
|
else
|
|
ret = bio_uncopy_user(bio);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* blk_rq_map_user_iov - map user data to a request, for REQ_TYPE_BLOCK_PC usage
|
|
* @q: request queue where request should be inserted
|
|
* @rq: request to map data to
|
|
* @map_data: pointer to the rq_map_data holding pages (if necessary)
|
|
* @iter: iovec iterator
|
|
* @gfp_mask: memory allocation flags
|
|
*
|
|
* Description:
|
|
* Data will be mapped directly for zero copy I/O, if possible. Otherwise
|
|
* a kernel bounce buffer is used.
|
|
*
|
|
* A matching blk_rq_unmap_user() must be issued at the end of I/O, while
|
|
* still in process context.
|
|
*
|
|
* Note: The mapped bio may need to be bounced through blk_queue_bounce()
|
|
* before being submitted to the device, as pages mapped may be out of
|
|
* reach. It's the callers responsibility to make sure this happens. The
|
|
* original bio must be passed back in to blk_rq_unmap_user() for proper
|
|
* unmapping.
|
|
*/
|
|
int blk_rq_map_user_iov(struct request_queue *q, struct request *rq,
|
|
struct rq_map_data *map_data,
|
|
const struct iov_iter *iter, gfp_t gfp_mask)
|
|
{
|
|
struct bio *bio;
|
|
int unaligned = 0;
|
|
struct iov_iter i;
|
|
struct iovec iov;
|
|
|
|
if (!iter || !iter->count)
|
|
return -EINVAL;
|
|
|
|
iov_for_each(iov, i, *iter) {
|
|
unsigned long uaddr = (unsigned long) iov.iov_base;
|
|
|
|
if (!iov.iov_len)
|
|
return -EINVAL;
|
|
|
|
/*
|
|
* Keep going so we check length of all segments
|
|
*/
|
|
if (uaddr & queue_dma_alignment(q))
|
|
unaligned = 1;
|
|
}
|
|
|
|
if (unaligned || (q->dma_pad_mask & iter->count) || map_data)
|
|
bio = bio_copy_user_iov(q, map_data, iter, gfp_mask);
|
|
else
|
|
bio = bio_map_user_iov(q, iter, gfp_mask);
|
|
|
|
if (IS_ERR(bio))
|
|
return PTR_ERR(bio);
|
|
|
|
if (map_data && map_data->null_mapped)
|
|
bio_set_flag(bio, BIO_NULL_MAPPED);
|
|
|
|
if (bio->bi_iter.bi_size != iter->count) {
|
|
/*
|
|
* Grab an extra reference to this bio, as bio_unmap_user()
|
|
* expects to be able to drop it twice as it happens on the
|
|
* normal IO completion path
|
|
*/
|
|
bio_get(bio);
|
|
bio_endio(bio);
|
|
__blk_rq_unmap_user(bio);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!bio_flagged(bio, BIO_USER_MAPPED))
|
|
rq->cmd_flags |= REQ_COPY_USER;
|
|
|
|
blk_queue_bounce(q, &bio);
|
|
bio_get(bio);
|
|
blk_rq_bio_prep(q, rq, bio);
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(blk_rq_map_user_iov);
|
|
|
|
int blk_rq_map_user(struct request_queue *q, struct request *rq,
|
|
struct rq_map_data *map_data, void __user *ubuf,
|
|
unsigned long len, gfp_t gfp_mask)
|
|
{
|
|
struct iovec iov;
|
|
struct iov_iter i;
|
|
int ret = import_single_range(rq_data_dir(rq), ubuf, len, &iov, &i);
|
|
|
|
if (unlikely(ret < 0))
|
|
return ret;
|
|
|
|
return blk_rq_map_user_iov(q, rq, map_data, &i, gfp_mask);
|
|
}
|
|
EXPORT_SYMBOL(blk_rq_map_user);
|
|
|
|
/**
|
|
* blk_rq_unmap_user - unmap a request with user data
|
|
* @bio: start of bio list
|
|
*
|
|
* Description:
|
|
* Unmap a rq previously mapped by blk_rq_map_user(). The caller must
|
|
* supply the original rq->bio from the blk_rq_map_user() return, since
|
|
* the I/O completion may have changed rq->bio.
|
|
*/
|
|
int blk_rq_unmap_user(struct bio *bio)
|
|
{
|
|
struct bio *mapped_bio;
|
|
int ret = 0, ret2;
|
|
|
|
while (bio) {
|
|
mapped_bio = bio;
|
|
if (unlikely(bio_flagged(bio, BIO_BOUNCED)))
|
|
mapped_bio = bio->bi_private;
|
|
|
|
ret2 = __blk_rq_unmap_user(mapped_bio);
|
|
if (ret2 && !ret)
|
|
ret = ret2;
|
|
|
|
mapped_bio = bio;
|
|
bio = bio->bi_next;
|
|
bio_put(mapped_bio);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(blk_rq_unmap_user);
|
|
|
|
/**
|
|
* blk_rq_map_kern - map kernel data to a request, for REQ_TYPE_BLOCK_PC usage
|
|
* @q: request queue where request should be inserted
|
|
* @rq: request to fill
|
|
* @kbuf: the kernel buffer
|
|
* @len: length of user data
|
|
* @gfp_mask: memory allocation flags
|
|
*
|
|
* Description:
|
|
* Data will be mapped directly if possible. Otherwise a bounce
|
|
* buffer is used. Can be called multiple times to append multiple
|
|
* buffers.
|
|
*/
|
|
int blk_rq_map_kern(struct request_queue *q, struct request *rq, void *kbuf,
|
|
unsigned int len, gfp_t gfp_mask)
|
|
{
|
|
int reading = rq_data_dir(rq) == READ;
|
|
unsigned long addr = (unsigned long) kbuf;
|
|
int do_copy = 0;
|
|
struct bio *bio;
|
|
int ret;
|
|
|
|
if (len > (queue_max_hw_sectors(q) << 9))
|
|
return -EINVAL;
|
|
if (!len || !kbuf)
|
|
return -EINVAL;
|
|
|
|
do_copy = !blk_rq_aligned(q, addr, len) || object_is_on_stack(kbuf);
|
|
if (do_copy)
|
|
bio = bio_copy_kern(q, kbuf, len, gfp_mask, reading);
|
|
else
|
|
bio = bio_map_kern(q, kbuf, len, gfp_mask);
|
|
|
|
if (IS_ERR(bio))
|
|
return PTR_ERR(bio);
|
|
|
|
if (!reading)
|
|
bio->bi_rw |= REQ_WRITE;
|
|
|
|
if (do_copy)
|
|
rq->cmd_flags |= REQ_COPY_USER;
|
|
|
|
ret = blk_rq_append_bio(q, rq, bio);
|
|
if (unlikely(ret)) {
|
|
/* request is too big */
|
|
bio_put(bio);
|
|
return ret;
|
|
}
|
|
|
|
blk_queue_bounce(q, &rq->bio);
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(blk_rq_map_kern);
|