summaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
Diffstat (limited to 'fs')
-rw-r--r--fs/bio.c38
1 files changed, 30 insertions, 8 deletions
diff --git a/fs/bio.c b/fs/bio.c
index 460554b07ff9..38d3e8023a07 100644
--- a/fs/bio.c
+++ b/fs/bio.c
@@ -313,7 +313,8 @@ int bio_get_nr_vecs(struct block_device *bdev)
}
static int __bio_add_page(request_queue_t *q, struct bio *bio, struct page
- *page, unsigned int len, unsigned int offset)
+ *page, unsigned int len, unsigned int offset,
+ unsigned short max_sectors)
{
int retried_segments = 0;
struct bio_vec *bvec;
@@ -327,7 +328,7 @@ static int __bio_add_page(request_queue_t *q, struct bio *bio, struct page
if (bio->bi_vcnt >= bio->bi_max_vecs)
return 0;
- if (((bio->bi_size + len) >> 9) > q->max_sectors)
+ if (((bio->bi_size + len) >> 9) > max_sectors)
return 0;
/*
@@ -386,6 +387,25 @@ static int __bio_add_page(request_queue_t *q, struct bio *bio, struct page
}
/**
+ * bio_add_pc_page - attempt to add page to bio
+ * @bio: destination bio
+ * @page: page to add
+ * @len: vec entry length
+ * @offset: vec entry offset
+ *
+ * Attempt to add a page to the bio_vec maplist. This can fail for a
+ * number of reasons, such as the bio being full or target block
+ * device limitations. The target block device must allow bio's
+ * smaller than PAGE_SIZE, so it is always possible to add a single
+ * page to an empty bio. This should only be used by REQ_PC bios.
+ */
+int bio_add_pc_page(request_queue_t *q, struct bio *bio, struct page *page,
+ unsigned int len, unsigned int offset)
+{
+ return __bio_add_page(q, bio, page, len, offset, q->max_hw_sectors);
+}
+
+/**
* bio_add_page - attempt to add page to bio
* @bio: destination bio
* @page: page to add
@@ -401,8 +421,8 @@ static int __bio_add_page(request_queue_t *q, struct bio *bio, struct page
int bio_add_page(struct bio *bio, struct page *page, unsigned int len,
unsigned int offset)
{
- return __bio_add_page(bdev_get_queue(bio->bi_bdev), bio, page,
- len, offset);
+ struct request_queue *q = bdev_get_queue(bio->bi_bdev);
+ return __bio_add_page(q, bio, page, len, offset, q->max_sectors);
}
struct bio_map_data {
@@ -514,7 +534,7 @@ struct bio *bio_copy_user(request_queue_t *q, unsigned long uaddr,
break;
}
- if (__bio_add_page(q, bio, page, bytes, 0) < bytes) {
+ if (bio_add_pc_page(q, bio, page, bytes, 0) < bytes) {
ret = -EINVAL;
break;
}
@@ -628,7 +648,8 @@ static struct bio *__bio_map_user_iov(request_queue_t *q,
/*
* sorry...
*/
- if (__bio_add_page(q, bio, pages[j], bytes, offset) < bytes)
+ if (bio_add_pc_page(q, bio, pages[j], bytes, offset) <
+ bytes)
break;
len -= bytes;
@@ -801,8 +822,8 @@ static struct bio *__bio_map_kern(request_queue_t *q, void *data,
if (bytes > len)
bytes = len;
- if (__bio_add_page(q, bio, virt_to_page(data), bytes,
- offset) < bytes)
+ if (bio_add_pc_page(q, bio, virt_to_page(data), bytes,
+ offset) < bytes)
break;
data += bytes;
@@ -1228,6 +1249,7 @@ EXPORT_SYMBOL(bio_clone);
EXPORT_SYMBOL(bio_phys_segments);
EXPORT_SYMBOL(bio_hw_segments);
EXPORT_SYMBOL(bio_add_page);
+EXPORT_SYMBOL(bio_add_pc_page);
EXPORT_SYMBOL(bio_get_nr_vecs);
EXPORT_SYMBOL(bio_map_user);
EXPORT_SYMBOL(bio_unmap_user);