mirror of
https://github.com/torvalds/linux.git
synced 2026-07-31 19:47:08 +02:00
Merge "virtual io: avoid setting swiotlb_force in virtual io mmio"
This commit is contained in:
commit
ea82617440
|
|
@ -148,6 +148,18 @@ config VIRTIO_MMIO
|
|||
|
||||
If unsure, say N.
|
||||
|
||||
config VIRTIO_MMIO_SWIOTLB
|
||||
bool "Instantiate swiotlb buffer pool for custom DMA ops"
|
||||
depends on VIRTIO_MMIO
|
||||
help
|
||||
Activating this feature results in custom DMA ops being
|
||||
setup up for virtio_mmio device, that uses swiotlb driver
|
||||
APIs to bounce buffers in and out of a pre-defined memory
|
||||
region. The pre-defined memory region is defined in
|
||||
device-tree (/swiotlb) and is indicative of memory that is
|
||||
shared with another virtual machine which is hosting virtio
|
||||
backend drivers.
|
||||
|
||||
config VIRTIO_MMIO_CMDLINE_DEVICES
|
||||
bool "Memory mapped virtio devices parameter parsing"
|
||||
depends on VIRTIO_MMIO
|
||||
|
|
|
|||
|
|
@ -69,7 +69,10 @@
|
|||
#include <linux/virtio_config.h>
|
||||
#include <uapi/linux/virtio_mmio.h>
|
||||
#include <linux/virtio_ring.h>
|
||||
|
||||
#ifdef CONFIG_VIRTIO_MMIO_SWIOTLB
|
||||
#include <linux/swiotlb.h>
|
||||
#include <linux/dma-direct.h>
|
||||
#endif
|
||||
|
||||
|
||||
/* The alignment to use between consumer and producer parts of vring.
|
||||
|
|
@ -81,6 +84,16 @@
|
|||
#define to_virtio_mmio_device(_plat_dev) \
|
||||
container_of(_plat_dev, struct virtio_mmio_device, vdev)
|
||||
|
||||
#ifdef CONFIG_VIRTIO_MMIO_SWIOTLB
|
||||
struct virtio_mem_pool {
|
||||
void *virt_base;
|
||||
dma_addr_t dma_base;
|
||||
size_t size;
|
||||
unsigned long *bitmap;
|
||||
spinlock_t lock;
|
||||
};
|
||||
#endif
|
||||
|
||||
struct virtio_mmio_device {
|
||||
struct virtio_device vdev;
|
||||
struct platform_device *pdev;
|
||||
|
|
@ -91,6 +104,9 @@ struct virtio_mmio_device {
|
|||
/* a list of queues so we can dispatch IRQs */
|
||||
spinlock_t lock;
|
||||
struct list_head virtqueues;
|
||||
#ifdef CONFIG_VIRTIO_MMIO_SWIOTLB
|
||||
struct virtio_mem_pool *mem_pool;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct virtio_mmio_vq_info {
|
||||
|
|
@ -590,8 +606,257 @@ static void virtio_mmio_release_dev(struct device *_d)
|
|||
devm_kfree(&pdev->dev, vm_dev);
|
||||
}
|
||||
|
||||
/* Platform device */
|
||||
#ifdef CONFIG_VIRTIO_MMIO_SWIOTLB
|
||||
static phys_addr_t virtio_swiotlb_base;
|
||||
static phys_addr_t virtio_swiotlb_dma_base;
|
||||
static size_t virtio_swiotlb_size;
|
||||
|
||||
static int get_swiotlb_base(struct device_node *np, phys_addr_t *base,
|
||||
phys_addr_t *dma_base, size_t *size)
|
||||
{
|
||||
const __be64 *val;
|
||||
int len;
|
||||
|
||||
val = of_get_property(np, "reg", &len);
|
||||
if (!val || len != 16)
|
||||
return -EINVAL;
|
||||
|
||||
*base = __be64_to_cpup(val);
|
||||
val++;
|
||||
*size = __be64_to_cpup(val);
|
||||
|
||||
if (!*base || !*size)
|
||||
return -EINVAL;
|
||||
|
||||
val = of_get_property(np, "dma_base", &len);
|
||||
if (!val || len != 8)
|
||||
return -EINVAL;
|
||||
*dma_base = __be64_to_cpup(val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init virtio_swiotlb_init(void)
|
||||
{
|
||||
void __iomem *vbase;
|
||||
int ret;
|
||||
unsigned long nslabs;
|
||||
phys_addr_t base;
|
||||
phys_addr_t dma_base;
|
||||
size_t size;
|
||||
struct device_node *np;
|
||||
|
||||
np = of_find_node_by_path("/swiotlb");
|
||||
if (!np)
|
||||
return 0;
|
||||
|
||||
ret = get_swiotlb_base(np, &base, &dma_base, &size);
|
||||
of_node_put(np);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
nslabs = (size >> IO_TLB_SHIFT);
|
||||
nslabs = ALIGN_DOWN(nslabs, IO_TLB_SEGSIZE);
|
||||
|
||||
if (!nslabs)
|
||||
return -EINVAL;
|
||||
|
||||
vbase = __ioremap(base, size, __pgprot(PROT_NORMAL));
|
||||
if (!vbase)
|
||||
return -EINVAL;
|
||||
|
||||
ret = swiotlb_late_init_with_tblpaddr(vbase, dma_base, nslabs);
|
||||
if (ret) {
|
||||
iounmap(vbase);
|
||||
return ret;
|
||||
}
|
||||
|
||||
virtio_swiotlb_base = base;
|
||||
virtio_swiotlb_dma_base = dma_base;
|
||||
virtio_swiotlb_size = size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *virtio_alloc_coherent(struct device *dev, size_t size,
|
||||
dma_addr_t *dma_handle, gfp_t flags, unsigned long attrs)
|
||||
{
|
||||
struct platform_device *pdev =
|
||||
container_of(dev, struct platform_device, dev);
|
||||
struct virtio_mmio_device *vm_dev = platform_get_drvdata(pdev);
|
||||
int pageno;
|
||||
unsigned long irq_flags;
|
||||
int order = get_order(size);
|
||||
void *ret = NULL;
|
||||
struct virtio_mem_pool *mem = vm_dev->mem_pool;
|
||||
|
||||
if (!mem || (size > (mem->size << PAGE_SHIFT)))
|
||||
return NULL;
|
||||
|
||||
spin_lock_irqsave(&mem->lock, irq_flags);
|
||||
|
||||
pageno = bitmap_find_free_region(mem->bitmap, mem->size, order);
|
||||
if (pageno >= 0) {
|
||||
*dma_handle = mem->dma_base + (pageno << PAGE_SHIFT);
|
||||
ret = mem->virt_base + (pageno << PAGE_SHIFT);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&mem->lock, irq_flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static void virtio_free_coherent(struct device *dev, size_t size, void *vaddr,
|
||||
dma_addr_t dma_handle, unsigned long attrs)
|
||||
{
|
||||
struct platform_device *pdev =
|
||||
container_of(dev, struct platform_device, dev);
|
||||
struct virtio_mmio_device *vm_dev = platform_get_drvdata(pdev);
|
||||
int pageno;
|
||||
unsigned long flags;
|
||||
struct virtio_mem_pool *mem = vm_dev->mem_pool;
|
||||
|
||||
if (!mem)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&mem->lock, flags);
|
||||
if (vaddr >= mem->virt_base &&
|
||||
vaddr < (mem->virt_base + (mem->size << PAGE_SHIFT))) {
|
||||
pageno = (vaddr - mem->virt_base) >> PAGE_SHIFT;
|
||||
bitmap_release_region(mem->bitmap, pageno, get_order(size));
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&mem->lock, flags);
|
||||
}
|
||||
|
||||
static dma_addr_t virtio_map_page(struct device *dev, struct page *page,
|
||||
unsigned long offset, size_t size,
|
||||
enum dma_data_direction dir,
|
||||
unsigned long attrs)
|
||||
{
|
||||
phys_addr_t phys = page_to_phys(page) + offset;
|
||||
|
||||
return swiotlb_map(dev, phys, size, dir, attrs);
|
||||
}
|
||||
|
||||
static void virtio_unmap_page(struct device *dev, dma_addr_t dev_addr,
|
||||
size_t size, enum dma_data_direction dir,
|
||||
unsigned long attrs)
|
||||
{
|
||||
BUG_ON(!is_swiotlb_buffer(dev, dev_addr));
|
||||
|
||||
swiotlb_tbl_unmap_single(dev, dev_addr, size, dir, attrs);
|
||||
}
|
||||
|
||||
size_t virtio_max_mapping_size(struct device *dev)
|
||||
{
|
||||
return SZ_4K;
|
||||
}
|
||||
|
||||
static const struct dma_map_ops virtio_dma_ops = {
|
||||
.alloc = virtio_alloc_coherent,
|
||||
.free = virtio_free_coherent,
|
||||
.map_page = virtio_map_page,
|
||||
.unmap_page = virtio_unmap_page,
|
||||
.max_mapping_size = virtio_max_mapping_size,
|
||||
};
|
||||
|
||||
static inline int
|
||||
get_ring_base(struct platform_device *pdev, phys_addr_t *ring_base,
|
||||
phys_addr_t *ring_dma_base, size_t *ring_size)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
const __be64 *val;
|
||||
int len;
|
||||
|
||||
if (!virtio_swiotlb_base)
|
||||
return 0;
|
||||
|
||||
val = of_get_property(pdev->dev.of_node, "ring_base", &len);
|
||||
if (!val || len != 24)
|
||||
return 0;
|
||||
|
||||
*ring_base = __be64_to_cpup(val);
|
||||
val++;
|
||||
*ring_dma_base = __be64_to_cpup(val);
|
||||
val++;
|
||||
*ring_size = __be64_to_cpup(val);
|
||||
|
||||
dev_dbg(dev, "ring_base %pK ring_dma_base %pK ring_size %llx\n",
|
||||
*ring_base, *ring_dma_base, *ring_size);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int setup_virtio_dma_ops(struct platform_device *pdev)
|
||||
{
|
||||
struct virtio_mmio_device *vm_dev = platform_get_drvdata(pdev);
|
||||
phys_addr_t ring_base, ring_dma_base;
|
||||
size_t ring_size, pages;
|
||||
struct virtio_mem_pool *vmem_pool;
|
||||
unsigned long bitmap_size;
|
||||
|
||||
if (!vm_dev || !get_ring_base(pdev, &ring_base,
|
||||
&ring_dma_base, &ring_size))
|
||||
return 0;
|
||||
|
||||
vmem_pool = devm_kzalloc(&pdev->dev, sizeof(struct virtio_mem_pool),
|
||||
GFP_KERNEL);
|
||||
if (!vmem_pool)
|
||||
return -ENOMEM;
|
||||
|
||||
pages = ring_size >> PAGE_SHIFT;
|
||||
if (!pages) {
|
||||
pr_err("%s: Ring size too small\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ULONG_MAX / sizeof(unsigned long) < BITS_TO_LONGS(pages)) {
|
||||
pr_err("%s: Ring size too large %lu\n", __func__, ring_size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
bitmap_size = BITS_TO_LONGS(pages) * sizeof(long);
|
||||
vmem_pool->bitmap = devm_kzalloc(&pdev->dev, bitmap_size, GFP_KERNEL);
|
||||
if (!vmem_pool->bitmap)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Note: Mapped as 'normal/cacheable' memory */
|
||||
vmem_pool->virt_base = __ioremap(ring_base, ring_size,
|
||||
__pgprot(PROT_NORMAL));
|
||||
if (!vmem_pool->virt_base) {
|
||||
pr_err("Unable to ioremap %pK size %lx\n",
|
||||
(void *)ring_base, ring_size);
|
||||
return -ENOMEM;
|
||||
}
|
||||
memset(vmem_pool->virt_base, 0, ring_size);
|
||||
|
||||
vmem_pool->dma_base = ring_dma_base;
|
||||
vmem_pool->size = pages;
|
||||
spin_lock_init(&vmem_pool->lock);
|
||||
vm_dev->mem_pool = vmem_pool;
|
||||
set_dma_ops(&pdev->dev, &virtio_dma_ops);
|
||||
|
||||
dev_dbg(&pdev->dev, "virtio_mem_pool: virt_base %llx pages %lx\n",
|
||||
vmem_pool->virt_base, pages);
|
||||
return 0;
|
||||
}
|
||||
#else /* CONFIG_VIRTIO_MMIO_SWIOTLB */
|
||||
|
||||
static inline int setup_virtio_dma_ops(struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int virtio_swiotlb_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_VIRTIO_MMIO_SWIOTLB */
|
||||
|
||||
/* Platform device */
|
||||
static int virtio_mmio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct virtio_mmio_device *vm_dev;
|
||||
|
|
@ -659,6 +924,12 @@ static int virtio_mmio_probe(struct platform_device *pdev)
|
|||
|
||||
platform_set_drvdata(pdev, vm_dev);
|
||||
|
||||
rc = setup_virtio_dma_ops(pdev);
|
||||
if (rc) {
|
||||
put_device(&vm_dev->vdev.dev);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = register_virtio_device(&vm_dev->vdev);
|
||||
if (rc)
|
||||
put_device(&vm_dev->vdev.dev);
|
||||
|
|
@ -830,6 +1101,12 @@ static struct platform_driver virtio_mmio_driver = {
|
|||
|
||||
static int __init virtio_mmio_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = virtio_swiotlb_init();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return platform_driver_register(&virtio_mmio_driver);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user