Skip to content

Commit

Permalink
usb:xhci:To improve performance,usb using lowmem for bulk xfer.
Browse files Browse the repository at this point in the history
Generate an usb low memory pool for usb 3.0 host
read/write transfer, default size is 8M.

Signed-off-by: minda.chen <minda.chen@starfivetech.com>
  • Loading branch information
mindachen1987 committed Jan 29, 2023
1 parent 7f21d0a commit ca21eac
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 3 deletions.
1 change: 1 addition & 0 deletions arch/riscv/boot/dts/starfive/jh7110-evb.dts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
};

&usbdrd30 {
xhci-lowmem-pool;
status = "okay";
};

Expand Down
4 changes: 3 additions & 1 deletion drivers/usb/core/hcd.c
Original file line number Diff line number Diff line change
Expand Up @@ -1452,7 +1452,9 @@ int usb_hcd_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
if (ret == 0)
urb->transfer_flags |= URB_MAP_LOCAL;
} else if (hcd_uses_dma(hcd)) {
if (urb->num_sgs) {
if (urb->transfer_flags & URB_MAP_LOCAL)
return ret;
else if (urb->num_sgs) {
int n;

/* We don't support sg for isoc transfers ! */
Expand Down
64 changes: 64 additions & 0 deletions drivers/usb/host/xhci-mem.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <linux/slab.h>
#include <linux/dmapool.h>
#include <linux/dma-mapping.h>
#include <linux/genalloc.h>

#include "xhci.h"
#include "xhci-trace.h"
Expand Down Expand Up @@ -1835,6 +1836,7 @@ void xhci_free_erst(struct xhci_hcd *xhci, struct xhci_erst *erst)
void xhci_mem_cleanup(struct xhci_hcd *xhci)
{
struct device *dev = xhci_to_hcd(xhci)->self.sysdev;
struct xhci_lowmem_pool *pool;
int i, j, num_ports;

cancel_delayed_work_sync(&xhci->cmd_timer);
Expand Down Expand Up @@ -1886,6 +1888,13 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
xhci_dbg_trace(xhci, trace_xhci_dbg_init,
"Freed medium stream array pool");

if (xhci->lowmem_pool.pool) {
pool = &xhci->lowmem_pool;
dma_free_coherent(dev, pool->size, (void *)pool->cached_base, pool->dma_addr);
gen_pool_destroy(pool->pool);
pool->pool = NULL;
}

if (xhci->dcbaa)
dma_free_coherent(dev, sizeof(*xhci->dcbaa),
xhci->dcbaa, xhci->dcbaa->dma);
Expand Down Expand Up @@ -2377,6 +2386,55 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
return 0;
}

int xhci_setup_local_lowmem(struct xhci_hcd *xhci, size_t size)
{
int err;
void *buffer;
dma_addr_t dma_addr;
struct usb_hcd *hcd = xhci_to_hcd(xhci);
struct xhci_lowmem_pool *pool = &xhci->lowmem_pool;

if (!pool->pool) {
/* minimal alloc one page */
pool->pool = gen_pool_create(PAGE_SHIFT, dev_to_node(hcd->self.sysdev));
if (IS_ERR(pool->pool))
return PTR_ERR(pool->pool);
}

buffer = dma_alloc_coherent(hcd->self.sysdev, size, &dma_addr,
GFP_KERNEL | GFP_DMA32);

if (IS_ERR(buffer)) {
err = PTR_ERR(buffer);
goto destroy_pool;
}

/*
* Here we pass a dma_addr_t but the arg type is a phys_addr_t.
* It's not backed by system memory and thus there's no kernel mapping
* for it.
*/
err = gen_pool_add_virt(pool->pool, (unsigned long)buffer,
dma_addr, size, dev_to_node(hcd->self.sysdev));
if (err < 0) {
dev_err(hcd->self.sysdev, "gen_pool_add_virt failed with %d\n",
err);
dma_free_coherent(hcd->self.sysdev, size, buffer, dma_addr);
goto destroy_pool;
}

pool->cached_base = (u64)buffer;
pool->dma_addr = dma_addr;

return 0;

destroy_pool:
gen_pool_destroy(pool->pool);
pool->pool = NULL;
return err;
}
EXPORT_SYMBOL_GPL(xhci_setup_local_lowmem);

int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
{
dma_addr_t dma;
Expand Down Expand Up @@ -2550,6 +2608,12 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)

xhci->isoc_bei_interval = AVOID_BEI_INTERVAL_MAX;

if (xhci->quirks & XHCI_LOCAL_BUFFER) {
if (xhci_setup_local_lowmem(xhci,
xhci->lowmem_pool.size))
goto fail;
}

/*
* XXX: Might need to set the Interrupter Moderation Register to
* something other than the default (~1ms minimum between interrupts).
Expand Down
8 changes: 8 additions & 0 deletions drivers/usb/host/xhci-plat.c
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,14 @@ static int xhci_plat_probe(struct platform_device *pdev)
if (device_property_read_bool(tmpdev, "quirk-broken-port-ped"))
xhci->quirks |= XHCI_BROKEN_PORT_PED;

if (device_property_read_bool(tmpdev, "xhci-lowmem-pool")) {
xhci->quirks |= XHCI_LOCAL_BUFFER;
if (device_property_read_u32(tmpdev, "lowmem-pool-size",
&xhci->lowmem_pool.size)) {
xhci->lowmem_pool.size = 8 << 20;
} else
xhci->lowmem_pool.size <<= 20;
}
device_property_read_u32(tmpdev, "imod-interval-ns",
&xhci->imod_interval);
}
Expand Down
3 changes: 2 additions & 1 deletion drivers/usb/host/xhci-ring.c
Original file line number Diff line number Diff line change
Expand Up @@ -3592,7 +3592,8 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,

full_len = urb->transfer_buffer_length;
/* If we have scatter/gather list, we use it. */
if (urb->num_sgs && !(urb->transfer_flags & URB_DMA_MAP_SINGLE)) {
if (urb->num_sgs && !(urb->transfer_flags & URB_DMA_MAP_SINGLE)
&& !(urb->transfer_flags & URB_MAP_LOCAL)) {
num_sgs = urb->num_mapped_sgs;
sg = urb->sg;
addr = (u64) sg_dma_address(sg);
Expand Down
57 changes: 56 additions & 1 deletion drivers/usb/host/xhci.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
#include <linux/slab.h>
#include <linux/dmi.h>
#include <linux/dma-mapping.h>
#include <linux/dma-map-ops.h>
#include <linux/genalloc.h>

#include "xhci.h"
#include "xhci-trace.h"
Expand Down Expand Up @@ -1380,6 +1382,55 @@ static void xhci_unmap_temp_buf(struct usb_hcd *hcd, struct urb *urb)
urb->transfer_buffer = NULL;
}

static void xhci_map_urb_local(struct usb_hcd *hcd, struct urb *urb,
gfp_t mem_flags)
{
void *buffer;
dma_addr_t dma_handle;
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
struct xhci_lowmem_pool *lowmem_pool = &xhci->lowmem_pool;

if (lowmem_pool->pool
&& (usb_endpoint_type(&urb->ep->desc) == USB_ENDPOINT_XFER_BULK)
&& (urb->transfer_buffer_length > PAGE_SIZE)
&& urb->num_sgs && urb->sg && (sg_phys(urb->sg) > 0xffffffff)) {
buffer = gen_pool_dma_alloc(lowmem_pool->pool,
urb->transfer_buffer_length, &dma_handle);
if (buffer) {
urb->transfer_dma = dma_handle;
urb->transfer_buffer = buffer;
urb->transfer_flags |= URB_MAP_LOCAL;
if (usb_urb_dir_out(urb))
sg_copy_to_buffer(urb->sg, urb->num_sgs,
(void *)buffer,
urb->transfer_buffer_length);
}
}

}

static void xhci_unmap_urb_local(struct usb_hcd *hcd, struct urb *urb)
{
dma_addr_t dma_handle;
u64 cached_buffer;
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
struct xhci_lowmem_pool *lowmem_pool = &xhci->lowmem_pool;

if (urb->transfer_flags & URB_MAP_LOCAL) {
dma_handle = urb->transfer_dma;
cached_buffer = lowmem_pool->cached_base +
((u32)urb->transfer_dma & (lowmem_pool->size - 1));
if (usb_urb_dir_in(urb))
sg_copy_from_buffer(urb->sg, urb->num_sgs,
(void *)cached_buffer, urb->transfer_buffer_length);
gen_pool_free(lowmem_pool->pool, (unsigned long)urb->transfer_buffer,
urb->transfer_buffer_length);
urb->transfer_flags &= ~URB_MAP_LOCAL;
urb->transfer_buffer = NULL;
}
}


/*
* Bypass the DMA mapping if URB is suitable for Immediate Transfer (IDT),
* we'll copy the actual data into the TRB address register. This is limited to
Expand All @@ -1400,9 +1451,11 @@ static int xhci_map_urb_for_dma(struct usb_hcd *hcd, struct urb *urb,
if (xhci_urb_temp_buffer_required(hcd, urb))
return xhci_map_temp_buffer(hcd, urb);
}
xhci_map_urb_local(hcd, urb, mem_flags);
return usb_hcd_map_urb_for_dma(hcd, urb, mem_flags);
}


static void xhci_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb)
{
struct xhci_hcd *xhci;
Expand All @@ -1415,8 +1468,10 @@ static void xhci_unmap_urb_for_dma(struct usb_hcd *hcd, struct urb *urb)

if ((xhci->quirks & XHCI_SG_TRB_CACHE_SIZE_QUIRK) && unmap_temp_buf)
xhci_unmap_temp_buf(hcd, urb);
else
else {
xhci_unmap_urb_local(hcd, urb);
usb_hcd_unmap_urb_for_dma(hcd, urb);
}
}

/**
Expand Down
11 changes: 11 additions & 0 deletions drivers/usb/host/xhci.h
Original file line number Diff line number Diff line change
Expand Up @@ -1747,6 +1747,13 @@ struct xhci_hub {
u8 min_rev;
};

struct xhci_lowmem_pool {
struct gen_pool *pool;
u64 cached_base;
dma_addr_t dma_addr;
unsigned int size;
};

/* There is one xhci_hcd structure per controller */
struct xhci_hcd {
struct usb_hcd *main_hcd;
Expand Down Expand Up @@ -1901,6 +1908,8 @@ struct xhci_hcd {
#define XHCI_BROKEN_D3COLD BIT_ULL(41)
#define XHCI_EP_CTX_BROKEN_DCS BIT_ULL(42)

#define XHCI_LOCAL_BUFFER BIT_ULL(63)

unsigned int num_active_eps;
unsigned int limit_active_eps;
struct xhci_port *hw_ports;
Expand Down Expand Up @@ -1928,6 +1937,8 @@ struct xhci_hcd {
struct list_head regset_list;

void *dbc;
struct xhci_lowmem_pool lowmem_pool;

/* platform-specific data -- must come last */
unsigned long priv[] __aligned(sizeof(s64));
};
Expand Down

0 comments on commit ca21eac

Please sign in to comment.