diff mbox series

[1/3] block: Allow mapping of vmalloc-ed buffers

Message ID 20190625024625.23976-2-damien.lemoal@wdc.com (mailing list archive)
State Superseded
Headers show
Series Fix zone revalidation memory allocation failures | expand

Commit Message

Damien Le Moal June 25, 2019, 2:46 a.m. UTC
To allow the SCSI subsystem scsi_execute_req() function to issue
requests using large buffers that are better allocated with vmalloc()
rather than kmalloc(), modify bio_map_kern() to allow passing a buffer
allocated with the vmalloc() function. To do so, simply test the buffer
address using is_vmalloc_addr() and use vmalloc_to_page() instead of
virt_to_page() to obtain the pages of vmalloc-ed buffers.

Fixes: 515ce6061312 ("scsi: sd_zbc: Fix sd_zbc_report_zones() buffer allocation")
Fixes: e76239a3748c ("block: add a report_zones method")
Cc: stable@vger.kernel.org
Signed-off-by: Damien Le Moal <damien.lemoal@wdc.com>
---
 block/bio.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

Comments

Chaitanya Kulkarni June 25, 2019, 3:24 a.m. UTC | #1
Looks good with one nit, can be done at the time of applying patch.

Reviewed-by: Chaitanya Kulkarni <chaitanya.kulkarni@wdc.com>

On 6/24/19 7:46 PM, Damien Le Moal wrote:
> To allow the SCSI subsystem scsi_execute_req() function to issue
> requests using large buffers that are better allocated with vmalloc()
> rather than kmalloc(), modify bio_map_kern() to allow passing a buffer
> allocated with the vmalloc() function. To do so, simply test the buffer
> address using is_vmalloc_addr() and use vmalloc_to_page() instead of
> virt_to_page() to obtain the pages of vmalloc-ed buffers.
> 
> Fixes: 515ce6061312 ("scsi: sd_zbc: Fix sd_zbc_report_zones() buffer allocation")
> Fixes: e76239a3748c ("block: add a report_zones method")
> Cc: stable@vger.kernel.org
> Signed-off-by: Damien Le Moal <damien.lemoal@wdc.com>
> ---
>   block/bio.c | 8 +++++++-
>   1 file changed, 7 insertions(+), 1 deletion(-)
> 
> diff --git a/block/bio.c b/block/bio.c
> index ce797d73bb43..05afcaf655f3 100644
> --- a/block/bio.c
> +++ b/block/bio.c
> @@ -1501,6 +1501,8 @@ struct bio *bio_map_kern(struct request_queue *q, void *data, unsigned int len,
>   	unsigned long end = (kaddr + len + PAGE_SIZE - 1) >> PAGE_SHIFT;
>   	unsigned long start = kaddr >> PAGE_SHIFT;
>   	const int nr_pages = end - start;
> +	bool is_vmalloc = is_vmalloc_addr(data); > +	struct page *page;
>   	int offset, i;
>   	struct bio *bio;
>   
> @@ -1518,7 +1520,11 @@ struct bio *bio_map_kern(struct request_queue *q, void *data, unsigned int len,
>   		if (bytes > len)
>   			bytes = len;
>   
> -		if (bio_add_pc_page(q, bio, virt_to_page(data), bytes,
> +		if (is_vmalloc)
nit:- Can we use is_vmalloc_addr() call directly so that
"if (is_vmalloc)" ->  "if (is_vmalloc_addr(data))" and remove is_vmalloc 
variable.
> +			page = vmalloc_to_page(data);
> +		else
> +			page = virt_to_page(data);
> +		if (bio_add_pc_page(q, bio, page, bytes,
>   				    offset) < bytes) {
>   			/* we don't support partial mappings */
>   			bio_put(bio);
>
Bart Van Assche June 25, 2019, 3:59 p.m. UTC | #2
On 6/24/19 8:24 PM, Chaitanya Kulkarni wrote:
> nit:- Can we use is_vmalloc_addr() call directly so that
> "if (is_vmalloc)" ->  "if (is_vmalloc_addr(data))" and remove is_vmalloc
> variable.
That would change a single call of is_vmalloc_addr() into multiple?

Bart.
Bart Van Assche June 25, 2019, 3:59 p.m. UTC | #3
On 6/24/19 7:46 PM, Damien Le Moal wrote:
> To allow the SCSI subsystem scsi_execute_req() function to issue
> requests using large buffers that are better allocated with vmalloc()
> rather than kmalloc(), modify bio_map_kern() to allow passing a buffer
> allocated with the vmalloc() function. To do so, simply test the buffer
> address using is_vmalloc_addr() and use vmalloc_to_page() instead of
> virt_to_page() to obtain the pages of vmalloc-ed buffers.
> 
> Fixes: 515ce6061312 ("scsi: sd_zbc: Fix sd_zbc_report_zones() buffer allocation")
> Fixes: e76239a3748c ("block: add a report_zones method")
> Cc: stable@vger.kernel.org
> Signed-off-by: Damien Le Moal <damien.lemoal@wdc.com>
> ---
>   block/bio.c | 8 +++++++-
>   1 file changed, 7 insertions(+), 1 deletion(-)
> 
> diff --git a/block/bio.c b/block/bio.c
> index ce797d73bb43..05afcaf655f3 100644
> --- a/block/bio.c
> +++ b/block/bio.c
> @@ -1501,6 +1501,8 @@ struct bio *bio_map_kern(struct request_queue *q, void *data, unsigned int len,
>   	unsigned long end = (kaddr + len + PAGE_SIZE - 1) >> PAGE_SHIFT;
>   	unsigned long start = kaddr >> PAGE_SHIFT;
>   	const int nr_pages = end - start;
> +	bool is_vmalloc = is_vmalloc_addr(data);
> +	struct page *page;
>   	int offset, i;
>   	struct bio *bio;
>   
> @@ -1518,7 +1520,11 @@ struct bio *bio_map_kern(struct request_queue *q, void *data, unsigned int len,
>   		if (bytes > len)
>   			bytes = len;
>   
> -		if (bio_add_pc_page(q, bio, virt_to_page(data), bytes,
> +		if (is_vmalloc)
> +			page = vmalloc_to_page(data);
> +		else
> +			page = virt_to_page(data);
> +		if (bio_add_pc_page(q, bio, page, bytes,
>   				    offset) < bytes) {
>   			/* we don't support partial mappings */
>   			bio_put(bio);

Reviewed-by: Bart Van Assche <bvanassche@acm.org>
Chaitanya Kulkarni June 25, 2019, 4:06 p.m. UTC | #4
On 06/25/2019 08:59 AM, Bart Van Assche wrote:
> On 6/24/19 8:24 PM, Chaitanya Kulkarni wrote:
>> nit:- Can we use is_vmalloc_addr() call directly so that
>> "if (is_vmalloc)" ->  "if (is_vmalloc_addr(data))" and remove is_vmalloc
>> variable.
> That would change a single call of is_vmalloc_addr() into multiple?

Well is_vmalloc_addr() it is an in-line helper with address comparison.

is it too expensive to have such a comparison in the loop ?

>
> Bart.
>
Christoph Hellwig June 25, 2019, 4:22 p.m. UTC | #5
On Tue, Jun 25, 2019 at 11:46:23AM +0900, Damien Le Moal wrote:
> To allow the SCSI subsystem scsi_execute_req() function to issue
> requests using large buffers that are better allocated with vmalloc()
> rather than kmalloc(), modify bio_map_kern() to allow passing a buffer
> allocated with the vmalloc() function. To do so, simply test the buffer
> address using is_vmalloc_addr() and use vmalloc_to_page() instead of
> virt_to_page() to obtain the pages of vmalloc-ed buffers.

This is broken on architectures with VIVT caches.  You need to flush
and invalidate the caches based on the virtual address on those before
performing DMA operations.
Damien Le Moal June 26, 2019, 1:38 a.m. UTC | #6
On 2019/06/26 1:06, Chaitanya Kulkarni wrote:
> On 06/25/2019 08:59 AM, Bart Van Assche wrote:
>> On 6/24/19 8:24 PM, Chaitanya Kulkarni wrote:
>>> nit:- Can we use is_vmalloc_addr() call directly so that
>>> "if (is_vmalloc)" ->  "if (is_vmalloc_addr(data))" and remove is_vmalloc
>>> variable.
>> That would change a single call of is_vmalloc_addr() into multiple?
> 
> Well is_vmalloc_addr() it is an in-line helper with address comparison.
> 
> is it too expensive to have such a comparison in the loop ?

Probably not, but I do not see the point in calling it for every page either
since the cost of the additional bool on the stack is likely also very low.
Chaitanya Kulkarni June 26, 2019, 4:19 a.m. UTC | #7
On 6/25/19 6:38 PM, Damien Le Moal wrote:
> On 2019/06/26 1:06, Chaitanya Kulkarni wrote:
>> On 06/25/2019 08:59 AM, Bart Van Assche wrote:
>>> On 6/24/19 8:24 PM, Chaitanya Kulkarni wrote:
>>>> nit:- Can we use is_vmalloc_addr() call directly so that
>>>> "if (is_vmalloc)" ->  "if (is_vmalloc_addr(data))" and remove is_vmalloc
>>>> variable.
>>> That would change a single call of is_vmalloc_addr() into multiple?
>>
>> Well is_vmalloc_addr() it is an in-line helper with address comparison.
>>
>> is it too expensive to have such a comparison in the loop ?
> 
> Probably not, but I do not see the point in calling it for every page either
> since the cost of the additional bool on the stack is likely also very low.
> 
Okay.
diff mbox series

Patch

diff --git a/block/bio.c b/block/bio.c
index ce797d73bb43..05afcaf655f3 100644
--- a/block/bio.c
+++ b/block/bio.c
@@ -1501,6 +1501,8 @@  struct bio *bio_map_kern(struct request_queue *q, void *data, unsigned int len,
 	unsigned long end = (kaddr + len + PAGE_SIZE - 1) >> PAGE_SHIFT;
 	unsigned long start = kaddr >> PAGE_SHIFT;
 	const int nr_pages = end - start;
+	bool is_vmalloc = is_vmalloc_addr(data);
+	struct page *page;
 	int offset, i;
 	struct bio *bio;
 
@@ -1518,7 +1520,11 @@  struct bio *bio_map_kern(struct request_queue *q, void *data, unsigned int len,
 		if (bytes > len)
 			bytes = len;
 
-		if (bio_add_pc_page(q, bio, virt_to_page(data), bytes,
+		if (is_vmalloc)
+			page = vmalloc_to_page(data);
+		else
+			page = virt_to_page(data);
+		if (bio_add_pc_page(q, bio, page, bytes,
 				    offset) < bytes) {
 			/* we don't support partial mappings */
 			bio_put(bio);