Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

block: Allow mapping of vmalloc-ed buffers

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 vmalloc().

To do so, detect vmalloc-ed buffers using is_vmalloc_addr(). For
vmalloc-ed buffers, flush the buffer using flush_kernel_vmap_range(),
use vmalloc_to_page() instead of virt_to_page() to obtain the pages of
the buffer, and invalidate the buffer addresses with
invalidate_kernel_vmap_range() on completion of read BIOs. This last
point is executed using the function bio_invalidate_vmalloc_pages()
which is defined only if the architecture defines
ARCH_HAS_FLUSH_KERNEL_DCACHE_PAGE, that is, if the architecture
actually needs the invalidation done.

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
Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>
Signed-off-by: Damien Le Moal <damien.lemoal@wdc.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Chaitanya Kulkarni <chaitanya.kulkarni@wdc.com>
Reviewed-by: Ming Lei <ming.lei@redhat.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>

authored by

Damien Le Moal and committed by
Jens Axboe
b4c5875d e7bf90e5

+27 -1
+27 -1
block/bio.c
··· 16 16 #include <linux/workqueue.h> 17 17 #include <linux/cgroup.h> 18 18 #include <linux/blk-cgroup.h> 19 + #include <linux/highmem.h> 19 20 20 21 #include <trace/events/block.h> 21 22 #include "blk.h" ··· 1442 1441 bio_put(bio); 1443 1442 } 1444 1443 1444 + static void bio_invalidate_vmalloc_pages(struct bio *bio) 1445 + { 1446 + #ifdef ARCH_HAS_FLUSH_KERNEL_DCACHE_PAGE 1447 + if (bio->bi_private && !op_is_write(bio_op(bio))) { 1448 + unsigned long i, len = 0; 1449 + 1450 + for (i = 0; i < bio->bi_vcnt; i++) 1451 + len += bio->bi_io_vec[i].bv_len; 1452 + invalidate_kernel_vmap_range(bio->bi_private, len); 1453 + } 1454 + #endif 1455 + } 1456 + 1445 1457 static void bio_map_kern_endio(struct bio *bio) 1446 1458 { 1459 + bio_invalidate_vmalloc_pages(bio); 1447 1460 bio_put(bio); 1448 1461 } 1449 1462 ··· 1478 1463 unsigned long end = (kaddr + len + PAGE_SIZE - 1) >> PAGE_SHIFT; 1479 1464 unsigned long start = kaddr >> PAGE_SHIFT; 1480 1465 const int nr_pages = end - start; 1466 + bool is_vmalloc = is_vmalloc_addr(data); 1467 + struct page *page; 1481 1468 int offset, i; 1482 1469 struct bio *bio; 1483 1470 1484 1471 bio = bio_kmalloc(gfp_mask, nr_pages); 1485 1472 if (!bio) 1486 1473 return ERR_PTR(-ENOMEM); 1474 + 1475 + if (is_vmalloc) { 1476 + flush_kernel_vmap_range(data, len); 1477 + bio->bi_private = data; 1478 + } 1487 1479 1488 1480 offset = offset_in_page(kaddr); 1489 1481 for (i = 0; i < nr_pages; i++) { ··· 1502 1480 if (bytes > len) 1503 1481 bytes = len; 1504 1482 1505 - if (bio_add_pc_page(q, bio, virt_to_page(data), bytes, 1483 + if (!is_vmalloc) 1484 + page = virt_to_page(data); 1485 + else 1486 + page = vmalloc_to_page(data); 1487 + if (bio_add_pc_page(q, bio, page, bytes, 1506 1488 offset) < bytes) { 1507 1489 /* we don't support partial mappings */ 1508 1490 bio_put(bio);