dma-buf-mapping.c 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * DMA BUF Mapping Helpers
  4. *
  5. */
  6. #include <linux/dma-buf-mapping.h>
  7. #include <linux/dma-resv.h>
  8. static struct scatterlist *fill_sg_entry(struct scatterlist *sgl, size_t length,
  9. dma_addr_t addr)
  10. {
  11. unsigned int len, nents;
  12. int i;
  13. nents = DIV_ROUND_UP(length, UINT_MAX);
  14. for (i = 0; i < nents; i++) {
  15. len = min_t(size_t, length, UINT_MAX);
  16. length -= len;
  17. /*
  18. * DMABUF abuses scatterlist to create a scatterlist
  19. * that does not have any CPU list, only the DMA list.
  20. * Always set the page related values to NULL to ensure
  21. * importers can't use it. The phys_addr based DMA API
  22. * does not require the CPU list for mapping or unmapping.
  23. */
  24. sg_set_page(sgl, NULL, 0, 0);
  25. sg_dma_address(sgl) = addr + (dma_addr_t)i * UINT_MAX;
  26. sg_dma_len(sgl) = len;
  27. sgl = sg_next(sgl);
  28. }
  29. return sgl;
  30. }
  31. static unsigned int calc_sg_nents(struct dma_iova_state *state,
  32. struct phys_vec *phys_vec, size_t nr_ranges,
  33. size_t size)
  34. {
  35. unsigned int nents = 0;
  36. size_t i;
  37. if (!state || !dma_use_iova(state)) {
  38. for (i = 0; i < nr_ranges; i++)
  39. nents += DIV_ROUND_UP(phys_vec[i].len, UINT_MAX);
  40. } else {
  41. /*
  42. * In IOVA case, there is only one SG entry which spans
  43. * for whole IOVA address space, but we need to make sure
  44. * that it fits sg->length, maybe we need more.
  45. */
  46. nents = DIV_ROUND_UP(size, UINT_MAX);
  47. }
  48. return nents;
  49. }
  50. /**
  51. * struct dma_buf_dma - holds DMA mapping information
  52. * @sgt: Scatter-gather table
  53. * @state: DMA IOVA state relevant in IOMMU-based DMA
  54. * @size: Total size of DMA transfer
  55. */
  56. struct dma_buf_dma {
  57. struct sg_table sgt;
  58. struct dma_iova_state *state;
  59. size_t size;
  60. };
  61. /**
  62. * dma_buf_phys_vec_to_sgt - Returns the scatterlist table of the attachment
  63. * from arrays of physical vectors. This funciton is intended for MMIO memory
  64. * only.
  65. * @attach: [in] attachment whose scatterlist is to be returned
  66. * @provider: [in] p2pdma provider
  67. * @phys_vec: [in] array of physical vectors
  68. * @nr_ranges: [in] number of entries in phys_vec array
  69. * @size: [in] total size of phys_vec
  70. * @dir: [in] direction of DMA transfer
  71. *
  72. * Returns sg_table containing the scatterlist to be returned; returns ERR_PTR
  73. * on error. May return -EINTR if it is interrupted by a signal.
  74. *
  75. * On success, the DMA addresses and lengths in the returned scatterlist are
  76. * PAGE_SIZE aligned.
  77. *
  78. * A mapping must be unmapped by using dma_buf_free_sgt().
  79. *
  80. * NOTE: This function is intended for exporters. If direct traffic routing is
  81. * mandatory exporter should call routing pci_p2pdma_map_type() before calling
  82. * this function.
  83. */
  84. struct sg_table *dma_buf_phys_vec_to_sgt(struct dma_buf_attachment *attach,
  85. struct p2pdma_provider *provider,
  86. struct phys_vec *phys_vec,
  87. size_t nr_ranges, size_t size,
  88. enum dma_data_direction dir)
  89. {
  90. unsigned int nents, mapped_len = 0;
  91. struct dma_buf_dma *dma;
  92. struct scatterlist *sgl;
  93. dma_addr_t addr;
  94. size_t i;
  95. int ret;
  96. dma_resv_assert_held(attach->dmabuf->resv);
  97. if (WARN_ON(!attach || !attach->dmabuf || !provider))
  98. /* This function is supposed to work on MMIO memory only */
  99. return ERR_PTR(-EINVAL);
  100. dma = kzalloc_obj(*dma);
  101. if (!dma)
  102. return ERR_PTR(-ENOMEM);
  103. switch (pci_p2pdma_map_type(provider, attach->dev)) {
  104. case PCI_P2PDMA_MAP_BUS_ADDR:
  105. /*
  106. * There is no need in IOVA at all for this flow.
  107. */
  108. break;
  109. case PCI_P2PDMA_MAP_THRU_HOST_BRIDGE:
  110. dma->state = kzalloc_obj(*dma->state);
  111. if (!dma->state) {
  112. ret = -ENOMEM;
  113. goto err_free_dma;
  114. }
  115. dma_iova_try_alloc(attach->dev, dma->state, 0, size);
  116. break;
  117. default:
  118. ret = -EINVAL;
  119. goto err_free_dma;
  120. }
  121. nents = calc_sg_nents(dma->state, phys_vec, nr_ranges, size);
  122. ret = sg_alloc_table(&dma->sgt, nents, GFP_KERNEL | __GFP_ZERO);
  123. if (ret)
  124. goto err_free_state;
  125. sgl = dma->sgt.sgl;
  126. for (i = 0; i < nr_ranges; i++) {
  127. if (!dma->state) {
  128. addr = pci_p2pdma_bus_addr_map(provider,
  129. phys_vec[i].paddr);
  130. } else if (dma_use_iova(dma->state)) {
  131. ret = dma_iova_link(attach->dev, dma->state,
  132. phys_vec[i].paddr, 0,
  133. phys_vec[i].len, dir,
  134. DMA_ATTR_MMIO);
  135. if (ret)
  136. goto err_unmap_dma;
  137. mapped_len += phys_vec[i].len;
  138. } else {
  139. addr = dma_map_phys(attach->dev, phys_vec[i].paddr,
  140. phys_vec[i].len, dir,
  141. DMA_ATTR_MMIO);
  142. ret = dma_mapping_error(attach->dev, addr);
  143. if (ret)
  144. goto err_unmap_dma;
  145. }
  146. if (!dma->state || !dma_use_iova(dma->state))
  147. sgl = fill_sg_entry(sgl, phys_vec[i].len, addr);
  148. }
  149. if (dma->state && dma_use_iova(dma->state)) {
  150. WARN_ON_ONCE(mapped_len != size);
  151. ret = dma_iova_sync(attach->dev, dma->state, 0, mapped_len);
  152. if (ret)
  153. goto err_unmap_dma;
  154. sgl = fill_sg_entry(sgl, mapped_len, dma->state->addr);
  155. }
  156. dma->size = size;
  157. /*
  158. * No CPU list included — set orig_nents = 0 so others can detect
  159. * this via SG table (use nents only).
  160. */
  161. dma->sgt.orig_nents = 0;
  162. /*
  163. * SGL must be NULL to indicate that SGL is the last one
  164. * and we allocated correct number of entries in sg_alloc_table()
  165. */
  166. WARN_ON_ONCE(sgl);
  167. return &dma->sgt;
  168. err_unmap_dma:
  169. if (!i || !dma->state) {
  170. ; /* Do nothing */
  171. } else if (dma_use_iova(dma->state)) {
  172. dma_iova_destroy(attach->dev, dma->state, mapped_len, dir,
  173. DMA_ATTR_MMIO);
  174. } else {
  175. for_each_sgtable_dma_sg(&dma->sgt, sgl, i)
  176. dma_unmap_phys(attach->dev, sg_dma_address(sgl),
  177. sg_dma_len(sgl), dir, DMA_ATTR_MMIO);
  178. }
  179. sg_free_table(&dma->sgt);
  180. err_free_state:
  181. kfree(dma->state);
  182. err_free_dma:
  183. kfree(dma);
  184. return ERR_PTR(ret);
  185. }
  186. EXPORT_SYMBOL_NS_GPL(dma_buf_phys_vec_to_sgt, "DMA_BUF");
  187. /**
  188. * dma_buf_free_sgt- unmaps the buffer
  189. * @attach: [in] attachment to unmap buffer from
  190. * @sgt: [in] scatterlist info of the buffer to unmap
  191. * @dir: [in] direction of DMA transfer
  192. *
  193. * This unmaps a DMA mapping for @attached obtained
  194. * by dma_buf_phys_vec_to_sgt().
  195. */
  196. void dma_buf_free_sgt(struct dma_buf_attachment *attach, struct sg_table *sgt,
  197. enum dma_data_direction dir)
  198. {
  199. struct dma_buf_dma *dma = container_of(sgt, struct dma_buf_dma, sgt);
  200. int i;
  201. dma_resv_assert_held(attach->dmabuf->resv);
  202. if (!dma->state) {
  203. ; /* Do nothing */
  204. } else if (dma_use_iova(dma->state)) {
  205. dma_iova_destroy(attach->dev, dma->state, dma->size, dir,
  206. DMA_ATTR_MMIO);
  207. } else {
  208. struct scatterlist *sgl;
  209. for_each_sgtable_dma_sg(sgt, sgl, i)
  210. dma_unmap_phys(attach->dev, sg_dma_address(sgl),
  211. sg_dma_len(sgl), dir, DMA_ATTR_MMIO);
  212. }
  213. sg_free_table(sgt);
  214. kfree(dma->state);
  215. kfree(dma);
  216. }
  217. EXPORT_SYMBOL_NS_GPL(dma_buf_free_sgt, "DMA_BUF");