Skip to content

Commit

Permalink
dma-mapping: add the device argument to dma_mapping_error()
Browse files Browse the repository at this point in the history
Add per-device dma_mapping_ops support for CONFIG_X86_64 as POWER
architecture does:

This enables us to cleanly fix the Calgary IOMMU issue that some devices
are not behind the IOMMU (http://lkml.org/lkml/2008/5/8/423).

I think that per-device dma_mapping_ops support would be also helpful for
KVM people to support PCI passthrough but Andi thinks that this makes it
difficult to support the PCI passthrough (see the above thread).  So I
CC'ed this to KVM camp.  Comments are appreciated.

A pointer to dma_mapping_ops to struct dev_archdata is added.  If the
pointer is non NULL, DMA operations in asm/dma-mapping.h use it.  If it's
NULL, the system-wide dma_ops pointer is used as before.

If it's useful for KVM people, I plan to implement a mechanism to register
a hook called when a new pci (or dma capable) device is created (it works
with hot plugging).  It enables IOMMUs to set up an appropriate
dma_mapping_ops per device.

The major obstacle is that dma_mapping_error doesn't take a pointer to the
device unlike other DMA operations.  So x86 can't have dma_mapping_ops per
device.  Note all the POWER IOMMUs use the same dma_mapping_error function
so this is not a problem for POWER but x86 IOMMUs use different
dma_mapping_error functions.

The first patch adds the device argument to dma_mapping_error.  The patch
is trivial but large since it touches lots of drivers and dma-mapping.h in
all the architecture.

This patch:

dma_mapping_error() doesn't take a pointer to the device unlike other DMA
operations.  So we can't have dma_mapping_ops per device.

Note that POWER already has dma_mapping_ops per device but all the POWER
IOMMUs use the same dma_mapping_error function.  x86 IOMMUs use device
argument.

[[email protected]: fix sge]
[[email protected]: fix svc_rdma]
[[email protected]: build fix]
[[email protected]: fix bnx2x]
[[email protected]: fix s2io]
[[email protected]: fix pasemi_mac]
[[email protected]: fix sdhci]
[[email protected]: build fix]
[[email protected]: fix sparc]
[[email protected]: fix ibmvscsi]
Signed-off-by: FUJITA Tomonori <[email protected]>
Cc: Muli Ben-Yehuda <[email protected]>
Cc: Andi Kleen <[email protected]>
Cc: Thomas Gleixner <[email protected]>
Cc: Ingo Molnar <[email protected]>
Cc: Avi Kivity <[email protected]>
Signed-off-by: Andrew Morton <[email protected]>
Signed-off-by: Linus Torvalds <[email protected]>
  • Loading branch information
fujita authored and torvalds committed Jul 26, 2008
1 parent c485b46 commit 8d8bb39
Show file tree
Hide file tree
Showing 76 changed files with 256 additions and 210 deletions.
4 changes: 2 additions & 2 deletions Documentation/DMA-API.txt
Original file line number Diff line number Diff line change
Expand Up @@ -298,10 +298,10 @@ recommended that you never use these unless you really know what the
cache width is.

int
dma_mapping_error(dma_addr_t dma_addr)
dma_mapping_error(struct device *dev, dma_addr_t dma_addr)

int
pci_dma_mapping_error(dma_addr_t dma_addr)
pci_dma_mapping_error(struct pci_dev *hwdev, dma_addr_t dma_addr)

In some circumstances dma_map_single and dma_map_page will fail to create
a mapping. A driver can check for these errors by testing the returned
Expand Down
2 changes: 1 addition & 1 deletion arch/arm/common/dmabounce.c
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ unmap_single(struct device *dev, dma_addr_t dma_addr, size_t size,
/*
* Trying to unmap an invalid mapping
*/
if (dma_mapping_error(dma_addr)) {
if (dma_mapping_error(dev, dma_addr)) {
dev_err(dev, "Trying to unmap invalid mapping\n");
return;
}
Expand Down
5 changes: 3 additions & 2 deletions arch/ia64/hp/common/hwsw_iommu.c
Original file line number Diff line number Diff line change
Expand Up @@ -186,9 +186,10 @@ hwsw_dma_supported (struct device *dev, u64 mask)
}

int
hwsw_dma_mapping_error (dma_addr_t dma_addr)
hwsw_dma_mapping_error(struct device *dev, dma_addr_t dma_addr)
{
return hwiommu_dma_mapping_error (dma_addr) || swiotlb_dma_mapping_error(dma_addr);
return hwiommu_dma_mapping_error(dev, dma_addr) ||
swiotlb_dma_mapping_error(dev, dma_addr);
}

EXPORT_SYMBOL(hwsw_dma_mapping_error);
Expand Down
2 changes: 1 addition & 1 deletion arch/ia64/hp/common/sba_iommu.c
Original file line number Diff line number Diff line change
Expand Up @@ -2147,7 +2147,7 @@ sba_dma_supported (struct device *dev, u64 mask)
}

int
sba_dma_mapping_error (dma_addr_t dma_addr)
sba_dma_mapping_error(struct device *dev, dma_addr_t dma_addr)
{
return 0;
}
Expand Down
2 changes: 1 addition & 1 deletion arch/ia64/sn/pci/pci_dma.c
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ void sn_dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg,
}
EXPORT_SYMBOL(sn_dma_sync_sg_for_device);

int sn_dma_mapping_error(dma_addr_t dma_addr)
int sn_dma_mapping_error(struct device *dev, dma_addr_t dma_addr)
{
return 0;
}
Expand Down
2 changes: 1 addition & 1 deletion arch/mips/mm/dma-default.c
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nele

EXPORT_SYMBOL(dma_sync_sg_for_device);

int dma_mapping_error(dma_addr_t dma_addr)
int dma_mapping_error(struct device *dev, dma_addr_t dma_addr)
{
return 0;
}
Expand Down
2 changes: 1 addition & 1 deletion arch/powerpc/platforms/cell/celleb_scc_pciex.c
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ static int __init scc_pciex_iowa_init(struct iowa_bus *bus, void *data)

dummy_page_da = dma_map_single(bus->phb->parent, dummy_page_va,
PAGE_SIZE, DMA_FROM_DEVICE);
if (dma_mapping_error(dummy_page_da)) {
if (dma_mapping_error(bus->phb->parent, dummy_page_da)) {
pr_err("PCIEX:Map dummy page failed.\n");
kfree(dummy_page_va);
return -1;
Expand Down
2 changes: 1 addition & 1 deletion arch/powerpc/platforms/cell/spider-pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ static int __init spiderpci_pci_setup_chip(struct pci_controller *phb,

dummy_page_da = dma_map_single(phb->parent, dummy_page_va,
PAGE_SIZE, DMA_FROM_DEVICE);
if (dma_mapping_error(dummy_page_da)) {
if (dma_mapping_error(phb->parent, dummy_page_da)) {
pr_err("SPIDER-IOWA:Map dummy page filed.\n");
kfree(dummy_page_va);
return -1;
Expand Down
2 changes: 1 addition & 1 deletion arch/powerpc/platforms/iseries/mf.c
Original file line number Diff line number Diff line change
Expand Up @@ -871,7 +871,7 @@ static int proc_mf_dump_cmdline(char *page, char **start, off_t off,
count = 256 - off;

dma_addr = iseries_hv_map(page, off + count, DMA_FROM_DEVICE);
if (dma_mapping_error(dma_addr))
if (dma_mapping_error(NULL, dma_addr))
return -ENOMEM;
memset(page, 0, off + count);
memset(&vsp_cmd, 0, sizeof(vsp_cmd));
Expand Down
2 changes: 1 addition & 1 deletion arch/x86/kernel/pci-calgary_64.c
Original file line number Diff line number Diff line change
Expand Up @@ -544,7 +544,7 @@ static void* calgary_alloc_coherent(struct device *dev, size_t size,
return ret;
}

static const struct dma_mapping_ops calgary_dma_ops = {
static struct dma_mapping_ops calgary_dma_ops = {
.alloc_coherent = calgary_alloc_coherent,
.map_single = calgary_map_single,
.unmap_single = calgary_unmap_single,
Expand Down
27 changes: 16 additions & 11 deletions arch/x86/kernel/pci-dma.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

static int forbid_dac __read_mostly;

const struct dma_mapping_ops *dma_ops;
struct dma_mapping_ops *dma_ops;
EXPORT_SYMBOL(dma_ops);

static int iommu_sac_force __read_mostly;
Expand Down Expand Up @@ -312,15 +312,17 @@ static int dma_release_coherent(struct device *dev, int order, void *vaddr)

int dma_supported(struct device *dev, u64 mask)
{
struct dma_mapping_ops *ops = get_dma_ops(dev);

#ifdef CONFIG_PCI
if (mask > 0xffffffff && forbid_dac > 0) {
dev_info(dev, "PCI: Disallowing DAC for device\n");
return 0;
}
#endif

if (dma_ops->dma_supported)
return dma_ops->dma_supported(dev, mask);
if (ops->dma_supported)
return ops->dma_supported(dev, mask);

/* Copied from i386. Doesn't make much sense, because it will
only work for pci_alloc_coherent.
Expand Down Expand Up @@ -367,6 +369,7 @@ void *
dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle,
gfp_t gfp)
{
struct dma_mapping_ops *ops = get_dma_ops(dev);
void *memory = NULL;
struct page *page;
unsigned long dma_mask = 0;
Expand Down Expand Up @@ -435,8 +438,8 @@ dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle,
/* Let low level make its own zone decisions */
gfp &= ~(GFP_DMA32|GFP_DMA);

if (dma_ops->alloc_coherent)
return dma_ops->alloc_coherent(dev, size,
if (ops->alloc_coherent)
return ops->alloc_coherent(dev, size,
dma_handle, gfp);
return NULL;
}
Expand All @@ -448,14 +451,14 @@ dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle,
}
}

if (dma_ops->alloc_coherent) {
if (ops->alloc_coherent) {
free_pages((unsigned long)memory, get_order(size));
gfp &= ~(GFP_DMA|GFP_DMA32);
return dma_ops->alloc_coherent(dev, size, dma_handle, gfp);
return ops->alloc_coherent(dev, size, dma_handle, gfp);
}

if (dma_ops->map_simple) {
*dma_handle = dma_ops->map_simple(dev, virt_to_phys(memory),
if (ops->map_simple) {
*dma_handle = ops->map_simple(dev, virt_to_phys(memory),
size,
PCI_DMA_BIDIRECTIONAL);
if (*dma_handle != bad_dma_address)
Expand All @@ -477,12 +480,14 @@ EXPORT_SYMBOL(dma_alloc_coherent);
void dma_free_coherent(struct device *dev, size_t size,
void *vaddr, dma_addr_t bus)
{
struct dma_mapping_ops *ops = get_dma_ops(dev);

int order = get_order(size);
WARN_ON(irqs_disabled()); /* for portability */
if (dma_release_coherent(dev, order, vaddr))
return;
if (dma_ops->unmap_single)
dma_ops->unmap_single(dev, bus, size, 0);
if (ops->unmap_single)
ops->unmap_single(dev, bus, size, 0);
free_pages((unsigned long)vaddr, order);
}
EXPORT_SYMBOL(dma_free_coherent);
Expand Down
3 changes: 1 addition & 2 deletions arch/x86/kernel/pci-gart_64.c
Original file line number Diff line number Diff line change
Expand Up @@ -692,8 +692,7 @@ static __init int init_k8_gatt(struct agp_kern_info *info)

extern int agp_amd64_init(void);

static const struct dma_mapping_ops gart_dma_ops = {
.mapping_error = NULL,
static struct dma_mapping_ops gart_dma_ops = {
.map_single = gart_map_single,
.map_simple = gart_map_simple,
.unmap_single = gart_unmap_single,
Expand Down
14 changes: 1 addition & 13 deletions arch/x86/kernel/pci-nommu.c
Original file line number Diff line number Diff line change
Expand Up @@ -72,21 +72,9 @@ static int nommu_map_sg(struct device *hwdev, struct scatterlist *sg,
return nents;
}

/* Make sure we keep the same behaviour */
static int nommu_mapping_error(dma_addr_t dma_addr)
{
#ifdef CONFIG_X86_32
return 0;
#else
return (dma_addr == bad_dma_address);
#endif
}


const struct dma_mapping_ops nommu_dma_ops = {
struct dma_mapping_ops nommu_dma_ops = {
.map_single = nommu_map_single,
.map_sg = nommu_map_sg,
.mapping_error = nommu_mapping_error,
.is_phys = 1,
};

Expand Down
2 changes: 1 addition & 1 deletion arch/x86/kernel/pci-swiotlb_64.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ swiotlb_map_single_phys(struct device *hwdev, phys_addr_t paddr, size_t size,
return swiotlb_map_single(hwdev, phys_to_virt(paddr), size, direction);
}

const struct dma_mapping_ops swiotlb_dma_ops = {
struct dma_mapping_ops swiotlb_dma_ops = {
.mapping_error = swiotlb_dma_mapping_error,
.alloc_coherent = swiotlb_alloc_coherent,
.free_coherent = swiotlb_free_coherent,
Expand Down
2 changes: 1 addition & 1 deletion drivers/firewire/fw-iso.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ fw_iso_buffer_init(struct fw_iso_buffer *buffer, struct fw_card *card,

address = dma_map_page(card->device, buffer->pages[i],
0, PAGE_SIZE, direction);
if (dma_mapping_error(address)) {
if (dma_mapping_error(card->device, address)) {
__free_page(buffer->pages[i]);
goto out_pages;
}
Expand Down
2 changes: 1 addition & 1 deletion drivers/firewire/fw-ohci.c
Original file line number Diff line number Diff line change
Expand Up @@ -953,7 +953,7 @@ at_context_queue_packet(struct context *ctx, struct fw_packet *packet)
payload_bus =
dma_map_single(ohci->card.device, packet->payload,
packet->payload_length, DMA_TO_DEVICE);
if (dma_mapping_error(payload_bus)) {
if (dma_mapping_error(ohci->card.device, payload_bus)) {
packet->ack = RCODE_SEND_ERROR;
return -1;
}
Expand Down
8 changes: 4 additions & 4 deletions drivers/firewire/fw-sbp2.c
Original file line number Diff line number Diff line change
Expand Up @@ -543,7 +543,7 @@ sbp2_send_management_orb(struct sbp2_logical_unit *lu, int node_id,
orb->response_bus =
dma_map_single(device->card->device, &orb->response,
sizeof(orb->response), DMA_FROM_DEVICE);
if (dma_mapping_error(orb->response_bus))
if (dma_mapping_error(device->card->device, orb->response_bus))
goto fail_mapping_response;

orb->request.response.high = 0;
Expand Down Expand Up @@ -577,7 +577,7 @@ sbp2_send_management_orb(struct sbp2_logical_unit *lu, int node_id,
orb->base.request_bus =
dma_map_single(device->card->device, &orb->request,
sizeof(orb->request), DMA_TO_DEVICE);
if (dma_mapping_error(orb->base.request_bus))
if (dma_mapping_error(device->card->device, orb->base.request_bus))
goto fail_mapping_request;

sbp2_send_orb(&orb->base, lu, node_id, generation,
Expand Down Expand Up @@ -1424,7 +1424,7 @@ sbp2_map_scatterlist(struct sbp2_command_orb *orb, struct fw_device *device,
orb->page_table_bus =
dma_map_single(device->card->device, orb->page_table,
sizeof(orb->page_table), DMA_TO_DEVICE);
if (dma_mapping_error(orb->page_table_bus))
if (dma_mapping_error(device->card->device, orb->page_table_bus))
goto fail_page_table;

/*
Expand Down Expand Up @@ -1509,7 +1509,7 @@ static int sbp2_scsi_queuecommand(struct scsi_cmnd *cmd, scsi_done_fn_t done)
orb->base.request_bus =
dma_map_single(device->card->device, &orb->request,
sizeof(orb->request), DMA_TO_DEVICE);
if (dma_mapping_error(orb->base.request_bus))
if (dma_mapping_error(device->card->device, orb->base.request_bus))
goto out;

sbp2_send_orb(&orb->base, lu, lu->tgt->node_id, lu->generation,
Expand Down
2 changes: 1 addition & 1 deletion drivers/infiniband/hw/ipath/ipath_sdma.c
Original file line number Diff line number Diff line change
Expand Up @@ -698,7 +698,7 @@ int ipath_sdma_verbs_send(struct ipath_devdata *dd,

addr = dma_map_single(&dd->pcidev->dev, tx->txreq.map_addr,
tx->map_len, DMA_TO_DEVICE);
if (dma_mapping_error(addr)) {
if (dma_mapping_error(&dd->pcidev->dev, addr)) {
ret = -EIO;
goto unlock;
}
Expand Down
6 changes: 3 additions & 3 deletions drivers/infiniband/hw/ipath/ipath_user_sdma.c
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ static int ipath_user_sdma_coalesce(const struct ipath_devdata *dd,

dma_addr = dma_map_page(&dd->pcidev->dev, page, 0, len,
DMA_TO_DEVICE);
if (dma_mapping_error(dma_addr)) {
if (dma_mapping_error(&dd->pcidev->dev, dma_addr)) {
ret = -ENOMEM;
goto free_unmap;
}
Expand Down Expand Up @@ -301,7 +301,7 @@ static int ipath_user_sdma_pin_pages(const struct ipath_devdata *dd,
pages[j], 0, flen, DMA_TO_DEVICE);
unsigned long fofs = addr & ~PAGE_MASK;

if (dma_mapping_error(dma_addr)) {
if (dma_mapping_error(&dd->pcidev->dev, dma_addr)) {
ret = -ENOMEM;
goto done;
}
Expand Down Expand Up @@ -508,7 +508,7 @@ static int ipath_user_sdma_queue_pkts(const struct ipath_devdata *dd,
if (page) {
dma_addr = dma_map_page(&dd->pcidev->dev,
page, 0, len, DMA_TO_DEVICE);
if (dma_mapping_error(dma_addr)) {
if (dma_mapping_error(&dd->pcidev->dev, dma_addr)) {
ret = -ENOMEM;
goto free_pbc;
}
Expand Down
2 changes: 1 addition & 1 deletion drivers/infiniband/hw/mthca/mthca_eq.c
Original file line number Diff line number Diff line change
Expand Up @@ -780,7 +780,7 @@ int mthca_map_eq_icm(struct mthca_dev *dev, u64 icm_virt)
return -ENOMEM;
dev->eq_table.icm_dma = pci_map_page(dev->pdev, dev->eq_table.icm_page, 0,
PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
if (pci_dma_mapping_error(dev->eq_table.icm_dma)) {
if (pci_dma_mapping_error(dev->pdev, dev->eq_table.icm_dma)) {
__free_page(dev->eq_table.icm_page);
return -ENOMEM;
}
Expand Down
2 changes: 1 addition & 1 deletion drivers/media/dvb/pluto2/pluto2.c
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ static int __devinit pluto_dma_map(struct pluto *pluto)
pluto->dma_addr = pci_map_single(pluto->pdev, pluto->dma_buf,
TS_DMA_BYTES, PCI_DMA_FROMDEVICE);

return pci_dma_mapping_error(pluto->dma_addr);
return pci_dma_mapping_error(pluto->pdev, pluto->dma_addr);
}

static void pluto_dma_unmap(struct pluto *pluto)
Expand Down
4 changes: 2 additions & 2 deletions drivers/mmc/host/sdhci.c
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,

host->align_addr = dma_map_single(mmc_dev(host->mmc),
host->align_buffer, 128 * 4, direction);
if (dma_mapping_error(host->align_addr))
if (dma_mapping_error(mmc_dev(host->mmc), host->align_addr))
goto fail;
BUG_ON(host->align_addr & 0x3);

Expand Down Expand Up @@ -439,7 +439,7 @@ static int sdhci_adma_table_pre(struct sdhci_host *host,

host->adma_addr = dma_map_single(mmc_dev(host->mmc),
host->adma_desc, (128 * 2 + 1) * 4, DMA_TO_DEVICE);
if (dma_mapping_error(host->align_addr))
if (dma_mapping_error(mmc_dev(host->mmc), host->align_addr))
goto unmap_entries;
BUG_ON(host->adma_addr & 0x3);

Expand Down
4 changes: 2 additions & 2 deletions drivers/net/arm/ep93xx_eth.c
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,7 @@ static int ep93xx_alloc_buffers(struct ep93xx_priv *ep)
goto err;

d = dma_map_single(NULL, page, PAGE_SIZE, DMA_FROM_DEVICE);
if (dma_mapping_error(d)) {
if (dma_mapping_error(NULL, d)) {
free_page((unsigned long)page);
goto err;
}
Expand All @@ -505,7 +505,7 @@ static int ep93xx_alloc_buffers(struct ep93xx_priv *ep)
goto err;

d = dma_map_single(NULL, page, PAGE_SIZE, DMA_TO_DEVICE);
if (dma_mapping_error(d)) {
if (dma_mapping_error(NULL, d)) {
free_page((unsigned long)page);
goto err;
}
Expand Down
4 changes: 2 additions & 2 deletions drivers/net/bnx2x_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -1020,7 +1020,7 @@ static inline int bnx2x_alloc_rx_sge(struct bnx2x *bp,

mapping = pci_map_page(bp->pdev, page, 0, BCM_PAGE_SIZE*PAGES_PER_SGE,
PCI_DMA_FROMDEVICE);
if (unlikely(dma_mapping_error(mapping))) {
if (unlikely(dma_mapping_error(&bp->pdev->dev, mapping))) {
__free_pages(page, PAGES_PER_SGE_SHIFT);
return -ENOMEM;
}
Expand Down Expand Up @@ -1048,7 +1048,7 @@ static inline int bnx2x_alloc_rx_skb(struct bnx2x *bp,

mapping = pci_map_single(bp->pdev, skb->data, bp->rx_buf_use_size,
PCI_DMA_FROMDEVICE);
if (unlikely(dma_mapping_error(mapping))) {
if (unlikely(dma_mapping_error(&bp->pdev->dev, mapping))) {
dev_kfree_skb(skb);
return -ENOMEM;
}
Expand Down
2 changes: 1 addition & 1 deletion drivers/net/cxgb3/sge.c
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ static inline int add_one_rx_buf(void *va, unsigned int len,
dma_addr_t mapping;

mapping = pci_map_single(pdev, va, len, PCI_DMA_FROMDEVICE);
if (unlikely(pci_dma_mapping_error(mapping)))
if (unlikely(pci_dma_mapping_error(pdev, mapping)))
return -ENOMEM;

pci_unmap_addr_set(sd, dma_addr, mapping);
Expand Down
Loading

0 comments on commit 8d8bb39

Please sign in to comment.