Commit 61426cf2 authored by David Brownell's avatar David Brownell Committed by Vojtech Pavlik

[PATCH] usbcore dma updates (and doc)

Attached is a patch leveraging some of the new generic dma stuff:

- Replaces dma mapping calls in usbcore with generic equivalents.
   This is a minor code shrink (which we'd hoped could happen).

- Pass dma mask along, so net drivers can notice it'd be good to
   set NETIF_F_HIGHDMA; or scsi ones can set highmem_io.  (Some
   Intel EHCI setups are able to support this kind of DMA.)

- Updates one net driver (usbnet) to set NETIF_F_HIGHDMA when
   appropriate, mostly as an example (since I can't test this).

- Provides Documentation/usb/dma.txt, describing current APIs.
   (Unchanged by this patch, except dma mask visibility.)

- Converted another info() to dev_info(), and likewise a couple
   dbg() to dev_dbg() conversions in the modified routine.

The number of FIXMEs was conserved: the generic API doesn't yet
fix the error reporting bugs in the PCI-specific mapping API.
parent 7013dfc3
In Linux 2.5 kernels (and later), USB device drivers have additional control
over how DMA may be used to perform I/O operations. The APIs are detailed
in the kernel usb programming guide (kerneldoc, from the source code).
API OVERVIEW
The big picture is that USB drivers can continue to ignore most DMA issues,
though they still must provide DMA-ready buffers (see DMA-mapping.txt).
That's how they've worked through the 2.4 (and earlier) kernels.
OR: they can now be DMA-aware.
- New calls enable DMA-aware drivers, letting them allocate dma buffers and
manage dma mappings for existing dma-ready buffers (see below).
- URBs have an additional "transfer_dma" field, as well as a transfer_flags
bit saying if it's valid. (Control requests also needed "setup_dma".)
- "usbcore" will map those DMA addresses, if a DMA-aware driver didn't do it
first and set URB_NO_DMA_MAP. HCDs don't manage dma mappings for urbs.
- There's a new "generic DMA API", parts of which are usable by USB device
drivers. Never use dma_set_mask() on any USB interface or device; that
would potentially break all devices sharing that bus.
ELIMINATING COPIES
It's good to avoid making CPUs copy data needlessly. The costs can add up,
and effects like cache-trashing can impose subtle penalties.
- When you're allocating a buffer for DMA purposes anyway, use the buffer
primitives. Think of them as kmalloc and kfree that give you the right
kind of addresses to store in urb->transfer_buffer and urb->transfer_dma,
while guaranteeing that hidden copies through DMA "bounce" buffers won't
slow things down. You'd also set URB_NO_DMA_MAP in urb->transfer_flags:
void *usb_buffer_alloc (struct usb_device *dev, size_t size,
int mem_flags, dma_addr_t *dma);
void usb_buffer_free (struct usb_device *dev, size_t size,
void *addr, dma_addr_t dma);
The memory buffer returned is "dma-coherent"; sometimes you might need to
force a consistent memory access ordering by using memory barriers. It's
not using a streaming DMA mapping, so it's good for small transfers on
systems where the I/O would otherwise tie up an IOMMU mapping.
Asking for 1/Nth of a page (as well as asking for N pages) is reasonably
space-efficient.
- Devices on some EHCI controllers could handle DMA to/from high memory.
Driver probe() routines can notice this using a generic DMA call, then
tell higher level code (network, scsi, etc) about it like this:
if (dma_supported (&intf->dev, 0xffffffffffffffffULL))
net->features |= NETIF_F_HIGHDMA;
That can eliminate dma bounce buffering of requests that originate (or
terminate) in high memory, in cases where the buffers aren't allocated
with usb_buffer_alloc() but instead are dma-mapped.
WORKING WITH EXISTING BUFFERS
Existing buffers aren't usable for DMA without first being mapped into the
DMA address space of the device.
- When you're using scatterlists, you can map everything at once. On some
systems, this kicks in an IOMMU and turns the scatterlists into single
DMA transactions:
int usb_buffer_map_sg (struct usb_device *dev, unsigned pipe,
struct scatterlist *sg, int nents);
void usb_buffer_dmasync_sg (struct usb_device *dev, unsigned pipe,
struct scatterlist *sg, int n_hw_ents);
void usb_buffer_unmap_sg (struct usb_device *dev, unsigned pipe,
struct scatterlist *sg, int n_hw_ents);
It's probably easier to use the new usb_sg_*() calls, which do the DMA
mapping and apply other tweaks to make scatterlist i/o be fast.
- Some drivers may prefer to work with the model that they're mapping large
buffers, synchronizing their safe re-use. (If there's no re-use, then let
usbcore do the map/unmap.) Large periodic transfers make good examples
here, since it's cheaper to just synchronize the buffer than to unmap it
each time an urb completes and then re-map it on during resubmission.
These calls all work with initialized urbs: urb->dev, urb->pipe,
urb->transfer_buffer, and urb->transfer_buffer_length must all be
valid when these calls are used:
struct urb *usb_buffer_map (struct urb *urb);
void usb_buffer_dmasync (struct urb *urb);
void usb_buffer_unmap (struct urb *urb);
The calls manage urb->transfer_dma for you, and set URB_NO_DMA_MAP so that
usbcore won't map or unmap the buffer.
......@@ -24,11 +24,14 @@
/*
* DMA-Consistent Buffers
* DMA-Coherent Buffers
*/
/* FIXME tune these based on pool statistics ... */
static const size_t pool_max [HCD_BUFFER_POOLS] = {
/* platforms without dma-friendly caches might need to
* prevent cacheline sharing...
*/
32,
128,
512,
......@@ -133,98 +136,3 @@ void hcd_buffer_free (
}
pci_free_consistent (hcd->pdev, size, addr, dma);
}
/*
* DMA-Mappings for arbitrary memory buffers
*/
int hcd_buffer_map (
struct usb_bus *bus,
void *addr,
dma_addr_t *dma,
size_t size,
int direction
) {
struct usb_hcd *hcd = bus->hcpriv;
// FIXME pci_map_single() has no standard failure mode!
*dma = pci_map_single (hcd->pdev, addr, size,
(direction == USB_DIR_IN)
? PCI_DMA_FROMDEVICE
: PCI_DMA_TODEVICE);
return 0;
}
void hcd_buffer_dmasync (
struct usb_bus *bus,
dma_addr_t dma,
size_t size,
int direction
) {
struct usb_hcd *hcd = bus->hcpriv;
pci_dma_sync_single (hcd->pdev, dma, size,
(direction == USB_DIR_IN)
? PCI_DMA_FROMDEVICE
: PCI_DMA_TODEVICE);
}
void hcd_buffer_unmap (
struct usb_bus *bus,
dma_addr_t dma,
size_t size,
int direction
) {
struct usb_hcd *hcd = bus->hcpriv;
pci_unmap_single (hcd->pdev, dma, size,
(direction == USB_DIR_IN)
? PCI_DMA_FROMDEVICE
: PCI_DMA_TODEVICE);
}
int hcd_buffer_map_sg (
struct usb_bus *bus,
struct scatterlist *sg,
int *n_hw_ents,
int nents,
int direction
) {
struct usb_hcd *hcd = bus->hcpriv;
// FIXME pci_map_sg() has no standard failure mode!
*n_hw_ents = pci_map_sg(hcd->pdev, sg, nents,
(direction == USB_DIR_IN)
? PCI_DMA_FROMDEVICE
: PCI_DMA_TODEVICE);
return 0;
}
void hcd_buffer_sync_sg (
struct usb_bus *bus,
struct scatterlist *sg,
int n_hw_ents,
int direction
) {
struct usb_hcd *hcd = bus->hcpriv;
pci_dma_sync_sg(hcd->pdev, sg, n_hw_ents,
(direction == USB_DIR_IN)
? PCI_DMA_FROMDEVICE
: PCI_DMA_TODEVICE);
}
void hcd_buffer_unmap_sg (
struct usb_bus *bus,
struct scatterlist *sg,
int n_hw_ents,
int direction
) {
struct usb_hcd *hcd = bus->hcpriv;
pci_unmap_sg(hcd->pdev, sg, n_hw_ents,
(direction == USB_DIR_IN)
? PCI_DMA_FROMDEVICE
: PCI_DMA_TODEVICE);
}
......@@ -138,7 +138,8 @@ int usb_hcd_pci_probe (struct pci_dev *dev, const struct pci_device_id *id)
hcd->pdev = dev;
hcd->self.bus_name = dev->slot_name;
hcd->product_desc = dev->dev.name;
hcd->controller = &dev->dev;
hcd->self.controller = &dev->dev;
hcd->controller = hcd->self.controller;
if ((retval = hcd_buffer_create (hcd)) != 0) {
clean_3:
......
......@@ -1031,19 +1031,19 @@ static int hcd_submit_urb (struct urb *urb, int mem_flags)
/* lower level hcd code should use *_dma exclusively */
if (!(urb->transfer_flags & URB_NO_DMA_MAP)) {
if (usb_pipecontrol (urb->pipe))
urb->setup_dma = pci_map_single (
hcd->pdev,
urb->setup_dma = dma_map_single (
hcd->controller,
urb->setup_packet,
sizeof (struct usb_ctrlrequest),
PCI_DMA_TODEVICE);
DMA_TO_DEVICE);
if (urb->transfer_buffer_length != 0)
urb->transfer_dma = pci_map_single (
hcd->pdev,
urb->transfer_dma = dma_map_single (
hcd->controller,
urb->transfer_buffer,
urb->transfer_buffer_length,
usb_pipein (urb->pipe)
? PCI_DMA_FROMDEVICE
: PCI_DMA_TODEVICE);
? DMA_FROM_DEVICE
: DMA_TO_DEVICE);
}
status = hcd->driver->urb_enqueue (hcd, urb, mem_flags);
......@@ -1265,12 +1265,6 @@ struct usb_operations usb_hcd_operations = {
.deallocate = hcd_free_dev,
.buffer_alloc = hcd_buffer_alloc,
.buffer_free = hcd_buffer_free,
.buffer_map = hcd_buffer_map,
.buffer_dmasync = hcd_buffer_dmasync,
.buffer_unmap = hcd_buffer_unmap,
.buffer_map_sg = hcd_buffer_map_sg,
.buffer_dmasync_sg = hcd_buffer_sync_sg,
.buffer_unmap_sg = hcd_buffer_unmap_sg,
};
EXPORT_SYMBOL (usb_hcd_operations);
......
......@@ -145,26 +145,6 @@ struct usb_operations {
dma_addr_t *dma);
void (*buffer_free)(struct usb_bus *bus, size_t size,
void *addr, dma_addr_t dma);
int (*buffer_map) (struct usb_bus *bus,
void *addr, dma_addr_t *dma,
size_t size, int direction);
void (*buffer_dmasync) (struct usb_bus *bus,
dma_addr_t dma,
size_t size, int direction);
void (*buffer_unmap) (struct usb_bus *bus,
dma_addr_t dma,
size_t size, int direction);
int (*buffer_map_sg) (struct usb_bus *bus,
struct scatterlist *sg, int *n_hw_ents,
int nents, int direction);
void (*buffer_dmasync_sg) (struct usb_bus *bus,
struct scatterlist *sg,
int n_hw_ents, int direction);
void (*buffer_unmap_sg) (struct usb_bus *bus,
struct scatterlist *sg,
int n_hw_ents, int direction);
};
/* each driver provides one of these, and hardware init support */
......@@ -248,23 +228,6 @@ void *hcd_buffer_alloc (struct usb_bus *bus, size_t size,
void hcd_buffer_free (struct usb_bus *bus, size_t size,
void *addr, dma_addr_t dma);
int hcd_buffer_map (struct usb_bus *bus,
void *addr, dma_addr_t *dma,
size_t size, int direction);
void hcd_buffer_dmasync (struct usb_bus *bus,
dma_addr_t dma,
size_t size, int direction);
void hcd_buffer_unmap (struct usb_bus *bus,
dma_addr_t dma,
size_t size, int direction);
int hcd_buffer_map_sg (struct usb_bus *bus, struct scatterlist *sg,
int *n_hw_ents, int nents, int direction);
void hcd_buffer_sync_sg (struct usb_bus *bus, struct scatterlist *sg,
int n_hw_ents, int direction);
void hcd_buffer_unmap_sg (struct usb_bus *bus, struct scatterlist *sg,
int n_hw_ents, int direction);
/* generic bus glue, needed for host controllers that don't use PCI */
extern struct usb_operations usb_hcd_operations;
extern void usb_hcd_irq (int irq, void *__hcd, struct pt_regs *r);
......
......@@ -41,6 +41,11 @@
#endif
#include <linux/usb.h>
#include <asm/io.h>
#include <asm/scatterlist.h>
#include <linux/mm.h>
#include <linux/dma-mapping.h>
#include "hcd.h"
#include "usb.h"
......@@ -803,7 +808,7 @@ void usb_disconnect(struct usb_device **pdev)
*pdev = NULL;
info("USB disconnect on device %d", dev->devnum);
dev_info (dev->dev, "USB disconnect, address %d\n", dev->devnum);
/* Free up all the children before we remove this device */
for (i = 0; i < USB_MAXCHILDREN; i++) {
......@@ -812,7 +817,7 @@ void usb_disconnect(struct usb_device **pdev)
usb_disconnect(child);
}
dbg ("unregistering interfaces on device %d", dev->devnum);
dev_dbg (dev->dev, "unregistering interfaces\n");
if (dev->actconfig) {
for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) {
struct usb_interface *interface = &dev->actconfig->interface[i];
......@@ -822,7 +827,7 @@ void usb_disconnect(struct usb_device **pdev)
}
}
dbg ("unregistering the device %d", dev->devnum);
dev_dbg (dev->dev, "unregistering device\n");
/* Free the device number and remove the /proc/bus/usb entry */
if (dev->devnum > 0) {
clear_bit(dev->devnum, dev->bus->devmap.devicemap);
......@@ -980,6 +985,9 @@ int usb_new_device(struct usb_device *dev, struct device *parent)
sprintf (&dev->dev.bus_id[0], "%d-%s",
dev->bus->busnum, dev->devpath);
/* dma masks come from the controller; readonly, except to hcd */
dev->dev.dma_mask = parent->dma_mask;
/* USB device state == default ... it's not usable yet */
/* USB 2.0 section 5.5.3 talks about ep0 maxpacket ...
......@@ -1104,6 +1112,7 @@ int usb_new_device(struct usb_device *dev, struct device *parent)
interface->dev.parent = &dev->dev;
interface->dev.driver = NULL;
interface->dev.bus = &usb_bus_type;
interface->dev.dma_mask = parent->dma_mask;
sprintf (&interface->dev.bus_id[0], "%d-%s:%d",
dev->bus->busnum, dev->devpath,
desc->bInterfaceNumber);
......@@ -1206,24 +1215,21 @@ void usb_buffer_free (
struct urb *usb_buffer_map (struct urb *urb)
{
struct usb_bus *bus;
struct usb_operations *op;
struct device *controller;
if (!urb
|| usb_pipecontrol (urb->pipe)
|| !urb->dev
|| !(bus = urb->dev->bus)
|| !(op = bus->op)
|| !op->buffer_map)
|| !(controller = bus->controller))
return 0;
if (op->buffer_map (bus,
urb->transfer_buffer,
&urb->transfer_dma,
urb->transfer_buffer_length,
urb->transfer_dma = dma_map_single (controller,
urb->transfer_buffer, urb->transfer_buffer_length,
usb_pipein (urb->pipe)
? USB_DIR_IN
: USB_DIR_OUT))
return 0;
? DMA_FROM_DEVICE : DMA_TO_DEVICE);
// FIXME generic api broken like pci, can't report errors
// if (urb->transfer_dma == DMA_ADDR_INVALID) return 0;
urb->transfer_flags |= URB_NO_DMA_MAP;
return urb;
}
......@@ -1235,22 +1241,19 @@ struct urb *usb_buffer_map (struct urb *urb)
void usb_buffer_dmasync (struct urb *urb)
{
struct usb_bus *bus;
struct usb_operations *op;
struct device *controller;
if (!urb
|| !(urb->transfer_flags & URB_NO_DMA_MAP)
|| !urb->dev
|| !(bus = urb->dev->bus)
|| !(op = bus->op)
|| !op->buffer_dmasync)
|| !(controller = bus->controller))
return;
op->buffer_dmasync (bus,
urb->transfer_dma,
urb->transfer_buffer_length,
dma_sync_single (controller,
urb->transfer_dma, urb->transfer_buffer_length,
usb_pipein (urb->pipe)
? USB_DIR_IN
: USB_DIR_OUT);
? DMA_FROM_DEVICE : DMA_TO_DEVICE);
}
/**
......@@ -1262,23 +1265,21 @@ void usb_buffer_dmasync (struct urb *urb)
void usb_buffer_unmap (struct urb *urb)
{
struct usb_bus *bus;
struct usb_operations *op;
struct device *controller;
if (!urb
|| !(urb->transfer_flags & URB_NO_DMA_MAP)
|| !urb->dev
|| !(bus = urb->dev->bus)
|| !(op = bus->op)
|| !op->buffer_unmap)
|| !(controller = bus->controller))
return;
op->buffer_unmap (bus,
urb->transfer_dma,
urb->transfer_buffer_length,
dma_unmap_single (controller,
urb->transfer_dma, urb->transfer_buffer_length,
usb_pipein (urb->pipe)
? USB_DIR_IN
: USB_DIR_OUT);
? DMA_FROM_DEVICE : DMA_TO_DEVICE);
}
/**
* usb_buffer_map_sg - create scatterlist DMA mapping(s) for an endpoint
* @dev: device to which the scatterlist will be mapped
......@@ -1297,6 +1298,7 @@ void usb_buffer_unmap (struct urb *urb)
* to complete before starting the next I/O. This is particularly easy
* to do with scatterlists. Just allocate and submit one URB for each DMA
* mapping entry returned, stopping on the first error or when all succeed.
* Better yet, use the usb_sg_*() calls, which do that (and more) for you.
*
* This call would normally be used when translating scatterlist requests,
* rather than usb_buffer_map(), since on some hardware (with IOMMUs) it
......@@ -1308,26 +1310,17 @@ int usb_buffer_map_sg (struct usb_device *dev, unsigned pipe,
struct scatterlist *sg, int nents)
{
struct usb_bus *bus;
struct usb_operations *op;
int n_hw_ents;
struct device *controller;
if (!dev
|| usb_pipecontrol (pipe)
|| !(bus = dev->bus)
|| !(op = bus->op)
|| !op->buffer_map_sg)
return -1;
if (op->buffer_map_sg (bus,
sg,
&n_hw_ents,
nents,
usb_pipein (pipe)
? USB_DIR_IN
: USB_DIR_OUT))
|| !(controller = bus->controller))
return -1;
return n_hw_ents;
// FIXME generic api broken like pci, can't report errors
return dma_map_sg (controller, sg, nents,
usb_pipein (pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
}
/**
......@@ -1344,20 +1337,15 @@ void usb_buffer_dmasync_sg (struct usb_device *dev, unsigned pipe,
struct scatterlist *sg, int n_hw_ents)
{
struct usb_bus *bus;
struct usb_operations *op;
struct device *controller;
if (!dev
|| !(bus = dev->bus)
|| !(op = bus->op)
|| !op->buffer_dmasync_sg)
|| !(controller = bus->controller))
return;
op->buffer_dmasync_sg (bus,
sg,
n_hw_ents,
usb_pipein (pipe)
? USB_DIR_IN
: USB_DIR_OUT);
dma_sync_sg (controller, sg, n_hw_ents,
usb_pipein (pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
}
/**
......@@ -1373,20 +1361,15 @@ void usb_buffer_unmap_sg (struct usb_device *dev, unsigned pipe,
struct scatterlist *sg, int n_hw_ents)
{
struct usb_bus *bus;
struct usb_operations *op;
struct device *controller;
if (!dev
|| !(bus = dev->bus)
|| !(op = bus->op)
|| !op->buffer_unmap_sg)
|| !(controller = bus->controller))
return;
op->buffer_unmap_sg (bus,
sg,
n_hw_ents,
usb_pipein (pipe)
? USB_DIR_IN
: USB_DIR_OUT);
dma_unmap_sg (controller, sg, n_hw_ents,
usb_pipein (pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
}
......
......@@ -405,9 +405,10 @@ static int ehci_start (struct usb_hcd *hcd)
* streaming mappings for I/O buffers, like pci_map_single(),
* can return segments above 4GB, if the device allows.
*
* NOTE: layered drivers can't yet tell when we enable that,
* so they can't pass this info along (like NETIF_F_HIGHDMA)
* (or like Scsi_Host.highmem_io) ... usb_bus.flags?
* NOTE: the dma mask is visible through dma_supported(), so
* drivers can pass this info along ... like NETIF_F_HIGHDMA,
* Scsi_Host.highmem_io, and so forth. It's readonly to all
* host side drivers though.
*/
if (HCC_64BIT_ADDR (hcc_params)) {
writel (0, &ehci->regs->segment);
......
......@@ -176,7 +176,8 @@ int usb_hcd_sa1111_probe (const struct hc_driver *driver,
hcd->irq = dev->irq[1];
hcd->regs = dev->mapbase;
hcd->pdev = SA1111_FAKE_PCIDEV;
hcd->controller = &dev->dev;
hcd->self.controller = &dev->dev;
hcd->controller = hcd->self.controller;
retval = hcd_buffer_create (hcd);
if (retval != 0) {
......
......@@ -146,6 +146,11 @@
#endif
#include <linux/usb.h>
#include <asm/io.h>
#include <asm/scatterlist.h>
#include <linux/mm.h>
#include <linux/dma-mapping.h>
/* minidrivers _could_ be individually configured */
#define CONFIG_USB_AN2720
......@@ -2169,9 +2174,13 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
memcpy (net->dev_addr, node_id, sizeof node_id);
// point-to-point link ... we always use Ethernet headers
// supports win32 interop and the bridge driver.
// supports win32 interop (some devices) and the bridge driver.
ether_setup (net);
// possible with some EHCI controllers
if (dma_supported (&udev->dev, 0xffffffffffffffffULL))
net->features |= NETIF_F_HIGHDMA;
net->change_mtu = usbnet_change_mtu;
net->get_stats = usbnet_get_stats;
net->hard_start_xmit = usbnet_start_xmit;
......
......@@ -28,14 +28,6 @@ static __inline__ void wait_ms(unsigned int ms)
mdelay(ms);
}
/*
* USB device number allocation bitmap. There's one bitmap
* per USB tree.
*/
struct usb_devmap {
unsigned long devicemap[128 / (8*sizeof(unsigned long))];
};
struct usb_device;
/*-------------------------------------------------------------------------*/
......@@ -159,10 +151,16 @@ int __usb_get_extra_descriptor(char *buffer, unsigned size,
struct usb_operations;
/* USB device number allocation bitmap */
struct usb_devmap {
unsigned long devicemap[128 / (8*sizeof(unsigned long))];
};
/*
* Allocated per bus we have
* Allocated per bus (tree of devices) we have:
*/
struct usb_bus {
struct device *controller; /* host/master side hardware */
int busnum; /* Bus number (in order of reg) */
char *bus_name; /* stable id (PCI slot_name etc) */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment