For HCs that have local memory, replace the current DMA API usage with a genalloc generic allocator to manage the mappings for these devices. To help users, introduce a new HCD API, usb_hcd_setup_local_mem() that will setup up the genalloc backing up the device local memory. It will be used in subsequent patches. This is in preparation for dropping the existing "coherent" dma mem declaration APIs. The current implementation was relying on a short circuit in the DMA API that in the end, was acting as an allocator for these type of devices.
Unified · Split
| @@ -45,6 +45,7 @@ config USB_ARCH_HAS_HCD · 44 unmodified lines | ||
| 45 | 45 | config USB |
| 46 | 46 | tristate "Support for Host-side USB" |
| 47 | 47 | depends on USB_ARCH_HAS_HCD |
| 48 | + select GENERIC_ALLOCATOR | |
| 48 | 49 | select USB_COMMON |
| 49 | 50 | select NLS # for UTF-8 strings |
| 50 | 51 | ---help--- |
| @@ -16,6 +16,7 @@ · 15 unmodified lines | ||
| 16 | 16 | #include <linux/io.h> |
| 17 | 17 | #include <linux/dma-mapping.h> |
| 18 | 18 | #include <linux/dmapool.h> |
| 19 | +#include <linux/genalloc.h> | |
| 19 | 20 | #include <linux/usb.h> |
| 20 | 21 | #include <linux/usb/hcd.h> |
| 21 | 22 | |
| @@ -124,6 +125,9 @@ void *hcd_buffer_alloc( · 102 unmodified lines | ||
| 124 | 125 | if (size == 0) |
| 125 | 126 | return NULL; |
| 126 | 127 | |
| 128 | + if (hcd->localmem_pool) | |
| 129 | + return gen_pool_dma_alloc(hcd->localmem_pool, size, dma); | |
| 130 | + | |
| 127 | 131 | /* some USB hosts just use PIO */ |
| 128 | 132 | if (!IS_ENABLED(CONFIG_HAS_DMA) || |
| 129 | 133 | (!is_device_dma_capable(bus->sysdev) && |
| @@ -152,6 +156,11 @@ void hcd_buffer_free( · 22 unmodified lines | ||
| 152 | 156 | if (!addr) |
| 153 | 157 | return; |
| 154 | 158 | |
| 159 | + if (hcd->localmem_pool) { | |
| 160 | + gen_pool_free(hcd->localmem_pool, (unsigned long)addr, size); | |
| 161 | + return; | |
| 162 | + } | |
| 163 | + | |
| 155 | 164 | if (!IS_ENABLED(CONFIG_HAS_DMA) || |
| 156 | 165 | (!is_device_dma_capable(bus->sysdev) && |
| 157 | 166 | !(hcd->driver->flags & HCD_LOCAL_MEM))) { |
| @@ -29,6 +29,8 @@ · 28 unmodified lines | ||
| 29 | 29 | #include <linux/workqueue.h> |
| 30 | 30 | #include <linux/pm_runtime.h> |
| 31 | 31 | #include <linux/types.h> |
| 32 | +#include <linux/genalloc.h> | |
| 33 | +#include <linux/io.h> | |
| 32 | 34 | |
| 33 | 35 | #include <linux/phy/phy.h> |
| 34 | 36 | #include <linux/usb.h> |
| @@ -3039,6 +3041,40 @@ usb_hcd_platform_shutdown(struct platform_device *dev) · 3004 unmodified lines | ||
| 3039 | 3041 | } |
| 3040 | 3042 | EXPORT_SYMBOL_GPL(usb_hcd_platform_shutdown); |
| 3041 | 3043 | |
| 3044 | +int usb_hcd_setup_local_mem(struct usb_hcd *hcd, phys_addr_t phys_addr, | |
| 3045 | + dma_addr_t dma, size_t size) | |
| 3046 | +{ | |
| 3047 | + int err; | |
| 3048 | + void *local_mem; | |
| 3049 | + | |
| 3050 | + hcd->localmem_pool = devm_gen_pool_create(hcd->self.sysdev, PAGE_SHIFT, | |
| 3051 | + dev_to_node(hcd->self.sysdev), | |
| 3052 | + dev_name(hcd->self.sysdev)); | |
| 3053 | + if (IS_ERR(hcd->localmem_pool)) | |
| 3054 | + return PTR_ERR(hcd->localmem_pool); | |
| 3055 | + | |
| 3056 | + local_mem = devm_memremap(hcd->self.sysdev, phys_addr, | |
| 3057 | + size, MEMREMAP_WC); | |
| 3058 | + if (!local_mem) | |
| 3059 | + return -ENOMEM; | |
| 3060 | + | |
| 3061 | + /* | |
| 3062 | + * Here we pass a dma_addr_t but the arg type is a phys_addr_t. | |
| 3063 | + * It's not backed by system memory and thus there's no kernel mapping | |
| 3064 | + * for it. | |
| 3065 | + */ | |
| 3066 | + err = gen_pool_add_virt(hcd->localmem_pool, (unsigned long)local_mem, | |
| 3067 | + dma, size, dev_to_node(hcd->self.sysdev)); | |
| 3068 | + if (err < 0) { | |
| 3069 | + dev_err(hcd->self.sysdev, "gen_pool_add_virt failed with %d\n", | |
| 3070 | + err); | |
| 3071 | + return err; | |
| 3072 | + } | |
| 3073 | + | |
| 3074 | + return 0; | |
| 3075 | +} | |
| 3076 | +EXPORT_SYMBOL_GPL(usb_hcd_setup_local_mem); | |
| 3077 | + | |
| 3042 | 3078 | /*-------------------------------------------------------------------------*/ |
| 3043 | 3079 | |
| 3044 | 3080 | #if IS_ENABLED(CONFIG_USB_MON) |
| @@ -40,6 +40,7 @@ · 39 unmodified lines | ||
| 40 | 40 | #include <linux/dmapool.h> |
| 41 | 41 | #include <linux/workqueue.h> |
| 42 | 42 | #include <linux/debugfs.h> |
| 43 | +#include <linux/genalloc.h> | |
| 43 | 44 | |
| 44 | 45 | #include <asm/io.h> |
| 45 | 46 | #include <asm/irq.h> |
| @@ -505,8 +506,15 @@ static int ohci_init (struct ohci_hcd *ohci) · 459 unmodified lines | ||
| 505 | 506 | timer_setup(&ohci->io_watchdog, io_watchdog_func, 0); |
| 506 | 507 | ohci->prev_frame_no = IO_WATCHDOG_OFF; |
| 507 | 508 | |
| 508 | − ohci->hcca = dma_alloc_coherent (hcd->self.controller, | |
| 509 | − sizeof(*ohci->hcca), &ohci->hcca_dma, GFP_KERNEL); | |
| 509 | + if (hcd->localmem_pool) | |
| 510 | + ohci->hcca = gen_pool_dma_alloc(hcd->localmem_pool, | |
| 511 | + sizeof(*ohci->hcca), | |
| 512 | + &ohci->hcca_dma); | |
| 513 | + else | |
| 514 | + ohci->hcca = dma_alloc_coherent(hcd->self.controller, | |
| 515 | + sizeof(*ohci->hcca), | |
| 516 | + &ohci->hcca_dma, | |
| 517 | + GFP_KERNEL); | |
| 510 | 518 | if (!ohci->hcca) |
| 511 | 519 | return -ENOMEM; |
| 512 | 520 | |
| @@ -990,9 +998,14 @@ static void ohci_stop (struct usb_hcd *hcd) · 477 unmodified lines | ||
| 990 | 998 | remove_debug_files (ohci); |
| 991 | 999 | ohci_mem_cleanup (ohci); |
| 992 | 1000 | if (ohci->hcca) { |
| 993 | − dma_free_coherent (hcd->self.controller, | |
| 994 | − sizeof *ohci->hcca, | |
| 995 | − ohci->hcca, ohci->hcca_dma); | |
| 1001 | + if (hcd->localmem_pool) | |
| 1002 | + gen_pool_free(hcd->localmem_pool, | |
| 1003 | + (unsigned long)ohci->hcca, | |
| 1004 | + sizeof(*ohci->hcca)); | |
| 1005 | + else | |
| 1006 | + dma_free_coherent(hcd->self.controller, | |
| 1007 | + sizeof(*ohci->hcca), | |
| 1008 | + ohci->hcca, ohci->hcca_dma); | |
| 996 | 1009 | ohci->hcca = NULL; |
| 997 | 1010 | ohci->hcca_dma = 0; |
| 998 | 1011 | } |
| @@ -36,6 +36,13 @@ static void ohci_hcd_init (struct ohci_hcd *ohci) · 35 unmodified lines | ||
| 36 | 36 | |
| 37 | 37 | static int ohci_mem_init (struct ohci_hcd *ohci) |
| 38 | 38 | { |
| 39 | + /* | |
| 40 | + * HCs with local memory allocate from localmem_pool so there's | |
| 41 | + * no need to create the below dma pools. | |
| 42 | + */ | |
| 43 | + if (ohci_to_hcd(ohci)->localmem_pool) | |
| 44 | + return 0; | |
| 45 | + | |
| 39 | 46 | ohci->td_cache = dma_pool_create ("ohci_td", |
| 40 | 47 | ohci_to_hcd(ohci)->self.controller, |
| 41 | 48 | sizeof (struct td), |
| @@ -84,8 +91,12 @@ td_alloc (struct ohci_hcd *hc, gfp_t mem_flags) · 42 unmodified lines | ||
| 84 | 91 | { |
| 85 | 92 | dma_addr_t dma; |
| 86 | 93 | struct td *td; |
| 94 | + struct usb_hcd *hcd = ohci_to_hcd(hc); | |
| 87 | 95 | |
| 88 | − td = dma_pool_zalloc (hc->td_cache, mem_flags, &dma); | |
| 96 | + if (hcd->localmem_pool) | |
| 97 | + td = gen_pool_dma_zalloc(hcd->localmem_pool, sizeof(*td), &dma); | |
| 98 | + else | |
| 99 | + td = dma_pool_zalloc(hc->td_cache, mem_flags, &dma); | |
| 89 | 100 | if (td) { |
| 90 | 101 | /* in case hc fetches it, make it look dead */ |
| 91 | 102 | td->hwNextTD = cpu_to_hc32 (hc, dma); |
| @@ -99,6 +110,7 @@ static void · 7 unmodified lines | ||
| 99 | 110 | td_free (struct ohci_hcd *hc, struct td *td) |
| 100 | 111 | { |
| 101 | 112 | struct td **prev = &hc->td_hash [TD_HASH_FUNC (td->td_dma)]; |
| 113 | + struct usb_hcd *hcd = ohci_to_hcd(hc); | |
| 102 | 114 | |
| 103 | 115 | while (*prev && *prev != td) |
| 104 | 116 | prev = &(*prev)->td_hash; |
| @@ -106,7 +118,12 @@ td_free (struct ohci_hcd *hc, struct td *td) · 1 unmodified line | ||
| 106 | 118 | *prev = td->td_hash; |
| 107 | 119 | else if ((td->hwINFO & cpu_to_hc32(hc, TD_DONE)) != 0) |
| 108 | 120 | ohci_dbg (hc, "no hash for td %p\n", td); |
| 109 | − dma_pool_free (hc->td_cache, td, td->td_dma); | |
| 121 | + | |
| 122 | + if (hcd->localmem_pool) | |
| 123 | + gen_pool_free(hcd->localmem_pool, (unsigned long)td, | |
| 124 | + sizeof(*td)); | |
| 125 | + else | |
| 126 | + dma_pool_free(hc->td_cache, td, td->td_dma); | |
| 110 | 127 | } |
| 111 | 128 | |
| 112 | 129 | /*-------------------------------------------------------------------------*/ |
| @@ -117,8 +134,12 @@ ed_alloc (struct ohci_hcd *hc, gfp_t mem_flags) · 4 unmodified lines | ||
| 117 | 134 | { |
| 118 | 135 | dma_addr_t dma; |
| 119 | 136 | struct ed *ed; |
| 137 | + struct usb_hcd *hcd = ohci_to_hcd(hc); | |
| 120 | 138 | |
| 121 | − ed = dma_pool_zalloc (hc->ed_cache, mem_flags, &dma); | |
| 139 | + if (hcd->localmem_pool) | |
| 140 | + ed = gen_pool_dma_zalloc(hcd->localmem_pool, sizeof(*ed), &dma); | |
| 141 | + else | |
| 142 | + ed = dma_pool_zalloc(hc->ed_cache, mem_flags, &dma); | |
| 122 | 143 | if (ed) { |
| 123 | 144 | INIT_LIST_HEAD (&ed->td_list); |
| 124 | 145 | ed->dma = dma; |
| @@ -129,6 +150,12 @@ ed_alloc (struct ohci_hcd *hc, gfp_t mem_flags) · 4 unmodified lines | ||
| 129 | 150 | static void |
| 130 | 151 | ed_free (struct ohci_hcd *hc, struct ed *ed) |
| 131 | 152 | { |
| 132 | − dma_pool_free (hc->ed_cache, ed, ed->dma); | |
| 153 | + struct usb_hcd *hcd = ohci_to_hcd(hc); | |
| 154 | + | |
| 155 | + if (hcd->localmem_pool) | |
| 156 | + gen_pool_free(hcd->localmem_pool, (unsigned long)ed, | |
| 157 | + sizeof(*ed)); | |
| 158 | + else | |
| 159 | + dma_pool_free(hc->ed_cache, ed, ed->dma); | |
| 133 | 160 | } |
| 134 | 161 | |
| @@ -385,6 +385,8 @@ struct ohci_hcd { · 384 unmodified lines | ||
| 385 | 385 | |
| 386 | 386 | /* |
| 387 | 387 | * memory management for queue data structures |
| 388 | + * | |
| 389 | + * @td_cache and @ed_cache are %NULL if &usb_hcd.localmem_pool is used. | |
| 388 | 390 | */ |
| 389 | 391 | struct dma_pool *td_cache; |
| 390 | 392 | struct dma_pool *ed_cache; |
| @@ -216,6 +216,9 @@ struct usb_hcd { · 215 unmodified lines | ||
| 216 | 216 | #define HC_IS_RUNNING(state) ((state) & __ACTIVE) |
| 217 | 217 | #define HC_IS_SUSPENDED(state) ((state) & __SUSPEND) |
| 218 | 218 | |
| 219 | + /* memory pool for HCs having local memory, or %NULL */ | |
| 220 | + struct gen_pool *localmem_pool; | |
| 221 | + | |
| 219 | 222 | /* more shared queuing code would be good; it should support |
| 220 | 223 | * smarter scheduling, handle transaction translators, etc; |
| 221 | 224 | * input size of periodic table to an interrupt scheduler. |
| @@ -461,6 +464,8 @@ extern int usb_add_hcd(struct usb_hcd *hcd, · 239 unmodified lines | ||
| 461 | 464 | unsigned int irqnum, unsigned long irqflags); |
| 462 | 465 | extern void usb_remove_hcd(struct usb_hcd *hcd); |
| 463 | 466 | extern int usb_hcd_find_raw_port_number(struct usb_hcd *hcd, int port1); |
| 467 | +int usb_hcd_setup_local_mem(struct usb_hcd *hcd, phys_addr_t phys_addr, | |
| 468 | + dma_addr_t dma, size_t size); | |
| 464 | 469 | |
| 465 | 470 | struct platform_device; |
| 466 | 471 | extern void usb_hcd_platform_shutdown(struct platform_device *dev); |