The SGT (Scatter Gather Table) DMA mapping type represents the existing sg_table/scatterlist based DMA mapping. It provides a sg_table based map/unmap interface that exactly matches how things work today.
dma_buf_sgt_exp_compat_match will be used in the next patch to transparently wrap an unaware exporter with a mapping type.
The SGT type handles the allow_peer2peer flag directly through matching logic. The importer indicates if it is willing to accept peer2peer and the exporter indicates if it requires peer2peer. A required peer2peer exporter will not match to an importer that does not accept peer2peer.
Signed-off-by: Jason Gunthorpe jgg@nvidia.com --- drivers/dma-buf/dma-buf-mapping.c | 95 ++++++++++++++++++++++++++++ include/linux/dma-buf-mapping.h | 101 ++++++++++++++++++++++++++++++ include/linux/dma-buf.h | 18 +++++- 3 files changed, 213 insertions(+), 1 deletion(-)
diff --git a/drivers/dma-buf/dma-buf-mapping.c b/drivers/dma-buf/dma-buf-mapping.c index 459c204cabb803..02f5cf8b3def40 100644 --- a/drivers/dma-buf/dma-buf-mapping.c +++ b/drivers/dma-buf/dma-buf-mapping.c @@ -6,6 +6,7 @@ #include <linux/dma-buf-mapping.h> #include <linux/dma-resv.h> #include <linux/dma-buf.h> +#include <linux/seq_file.h>
static struct scatterlist *fill_sg_entry(struct scatterlist *sgl, size_t length, dma_addr_t addr) @@ -292,3 +293,97 @@ int dma_buf_match_mapping(struct dma_buf_match_args *args, return -EINVAL; } EXPORT_SYMBOL_NS_GPL(dma_buf_match_mapping, "DMA_BUF"); + +static int dma_buf_sgt_match(struct dma_buf *dmabuf, + const struct dma_buf_mapping_match *exp, + const struct dma_buf_mapping_match *imp) +{ + switch (exp->sgt_data.exporter_requires_p2p) { + case DMA_SGT_NO_P2P: + return 0; + case DMA_SGT_EXPORTER_REQUIRES_P2P_DISTANCE: + if (WARN_ON(!exp->sgt_data.exporting_p2p_device) || + imp->sgt_data.importer_accepts_p2p != + DMA_SGT_IMPORTER_ACCEPTS_P2P) + return -EOPNOTSUPP; + if (pci_p2pdma_distance(exp->sgt_data.exporting_p2p_device, + imp->sgt_data.importing_dma_device, + true) < 0) + return -EOPNOTSUPP; + return 0; + } + return 0; +} + +static inline void +dma_buf_sgt_finish_match(struct dma_buf_match_args *args, + const struct dma_buf_mapping_match *exp, + const struct dma_buf_mapping_match *imp) +{ + struct dma_buf_attachment *attach = args->attach; + + attach->map_type = (struct dma_buf_mapping_match) { + .type = &dma_buf_mapping_sgt_type, + .exp_ops = exp->exp_ops, + .sgt_data = { + .importing_dma_device = imp->sgt_data.importing_dma_device, + /* exporting_p2p_device is left opaque */ + .importer_accepts_p2p = imp->sgt_data.importer_accepts_p2p, + .exporter_requires_p2p = exp->sgt_data.exporter_requires_p2p, + }, + }; + + /* + * Setup the SGT type variables stored in attach because importers and + * exporters that do not natively use mappings expect them to be there. + * When converting to use mappings users should use the match versions + * of these instead. + */ + attach->dev = imp->sgt_data.importing_dma_device; + attach->peer2peer = attach->map_type.sgt_data.importer_accepts_p2p == + DMA_SGT_IMPORTER_ACCEPTS_P2P; +} + +static void dma_buf_sgt_debugfs_dump(struct seq_file *s, + struct dma_buf_attachment *attach) +{ + seq_printf(s, " %s", dev_name(dma_buf_sgt_dma_device(attach))); +} + +struct dma_buf_mapping_type dma_buf_mapping_sgt_type = { + .name = "DMA Mapped Scatter Gather Table", + .match = dma_buf_sgt_match, + .finish_match = dma_buf_sgt_finish_match, + .debugfs_dump = dma_buf_sgt_debugfs_dump, +}; +EXPORT_SYMBOL_NS_GPL(dma_buf_mapping_sgt_type, "DMA_BUF"); + +static struct sg_table * +dma_buf_sgt_compat_map_dma_buf(struct dma_buf_attachment *attach, + enum dma_data_direction dir) +{ + return attach->dmabuf->ops->map_dma_buf(attach, dir); +} + +static void dma_buf_sgt_compat_unmap_dma_buf(struct dma_buf_attachment *attach, + struct sg_table *sgt, + enum dma_data_direction dir) +{ + attach->dmabuf->ops->unmap_dma_buf(attach, sgt, dir); +} + +/* Route the classic map/unmap ops through the exp ops for old importers */ +static const struct dma_buf_mapping_sgt_exp_ops dma_buf_sgt_compat_exp_ops = { + .map_dma_buf = dma_buf_sgt_compat_map_dma_buf, + .unmap_dma_buf = dma_buf_sgt_compat_unmap_dma_buf, +}; + +/* + * This mapping type is used for unaware exporters that do not support + * match_mapping(). It wraps the dma_buf ops for SGT mappings into a mapping + * type so aware importers can transparently work with unaware exporters. This + * does not require p2p because old exporters will check it through the + * attach->peer2peer mechanism. + */ +const struct dma_buf_mapping_match dma_buf_sgt_exp_compat_match = + DMA_BUF_EMAPPING_SGT(&dma_buf_sgt_compat_exp_ops); diff --git a/include/linux/dma-buf-mapping.h b/include/linux/dma-buf-mapping.h index 080ccbf3a3f8b8..360a7fe0b098be 100644 --- a/include/linux/dma-buf-mapping.h +++ b/include/linux/dma-buf-mapping.h @@ -12,6 +12,12 @@ struct dma_buf; struct dma_buf_attachment; struct dma_buf_mapping_exp_ops;
+enum dma_sgt_requires_p2p { + DMA_SGT_NO_P2P = 0, + DMA_SGT_EXPORTER_REQUIRES_P2P_DISTANCE, + DMA_SGT_IMPORTER_ACCEPTS_P2P, +}; + /* Type tag for all mapping operations */ struct dma_buf_mapping_exp_ops {};
@@ -90,4 +96,99 @@ int dma_buf_match_mapping(struct dma_buf_match_args *args, const struct dma_buf_mapping_match *exp_mappings, size_t exp_len);
+/* + * DMA Mapped Scatterlist Type + * + * When this type is matched the map/unmap functions are: + * + * dma_buf_map_attachment() + * dma_buf_unmap_attachment() + * + * The struct sg_table returned by those functions has only the DMA portions + * available. The caller must not try to use the struct page * information. + * + * importing_dma_device is passed to the DMA API to provide the dma_addr_t's. + */ +extern struct dma_buf_mapping_type dma_buf_mapping_sgt_type; + +struct dma_buf_mapping_sgt_exp_ops { + struct dma_buf_mapping_exp_ops ops; + struct sg_table *(*map_dma_buf)(struct dma_buf_attachment *attach, + enum dma_data_direction dir); + void (*unmap_dma_buf)(struct dma_buf_attachment *attach, + struct sg_table *sgt, + enum dma_data_direction dir); +}; + +/** + * dma_buf_sgt_dma_device - Return the device to use for DMA mapping + * @attach: sgt mapping type attachment + * + * Called by the exporter to get the struct device to pass to the DMA API + * during map and unmap callbacks. + */ +static inline struct device * +dma_buf_sgt_dma_device(struct dma_buf_attachment *attach) +{ + if (attach->map_type.type != &dma_buf_mapping_sgt_type) + return NULL; + return attach->map_type.sgt_data.importing_dma_device; +} + +/** + * dma_buf_sgt_p2p_allowed - True if MMIO memory can be used peer to peer + * @attach: sgt mapping type attachment + * + * Should be called by exporters, returns true if the exporter's + * DMA_SGT_EXPORTER_REQUIRES_P2P_DISTANCE was matched. + */ +static inline bool dma_buf_sgt_p2p_allowed(struct dma_buf_attachment *attach) +{ + if (attach->map_type.type != &dma_buf_mapping_sgt_type) + return false; + return attach->map_type.sgt_data.exporter_requires_p2p == + DMA_SGT_EXPORTER_REQUIRES_P2P_DISTANCE; +} + +static inline const struct dma_buf_mapping_sgt_exp_ops * +dma_buf_get_sgt_ops(struct dma_buf_attachment *attach) +{ + if (attach->map_type.type != &dma_buf_mapping_sgt_type) + return NULL; + return container_of(attach->map_type.exp_ops, + struct dma_buf_mapping_sgt_exp_ops, ops); +} + +static inline struct dma_buf_mapping_match +DMA_BUF_IMAPPING_SGT(struct device *importing_dma_device, + enum dma_sgt_requires_p2p importer_accepts_p2p) +{ + return (struct dma_buf_mapping_match){ + .type = &dma_buf_mapping_sgt_type, + .sgt_data = { .importing_dma_device = importing_dma_device, + .importer_accepts_p2p = importer_accepts_p2p }, + }; +} +#define DMA_BUF_EMAPPING_SGT(_exp_ops) \ + ((struct dma_buf_mapping_match){ .type = &dma_buf_mapping_sgt_type, \ + .exp_ops = &((_exp_ops)->ops) }) + +/* + * Only matches if the importing device is P2P capable and the P2P subsystem + * says P2P is possible from p2p_device. + */ +static inline struct dma_buf_mapping_match +DMA_BUF_EMAPPING_SGT_P2P(const struct dma_buf_mapping_sgt_exp_ops *exp_ops, + struct pci_dev *p2p_device) +{ + struct dma_buf_mapping_match match = DMA_BUF_EMAPPING_SGT(exp_ops); + + match.sgt_data.exporter_requires_p2p = + DMA_SGT_EXPORTER_REQUIRES_P2P_DISTANCE; + match.sgt_data.exporting_p2p_device = p2p_device; + return match; +} + +extern const struct dma_buf_mapping_match dma_buf_sgt_exp_compat_match; + #endif diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h index a2b01b13026810..3bcd1d6d150188 100644 --- a/include/linux/dma-buf.h +++ b/include/linux/dma-buf.h @@ -30,6 +30,7 @@ struct dma_buf_attachment; struct dma_buf_mapping_type; struct dma_buf_mapping_exp_ops;
+ /* * Match items are generated by the importer using the DMA_BUF_IMAPPING_*() and * the exporter using the DMA_BUF_EMAPPING_*() functions. Each mapping type @@ -39,7 +40,22 @@ struct dma_buf_mapping_match { const struct dma_buf_mapping_type *type; const struct dma_buf_mapping_exp_ops *exp_ops; union { - /* Each mapping_type has unique match parameters here */ + /* For dma_buf_mapping_sgt_type */ + struct { + struct device *importing_dma_device; + /* Only used if DMA_SGT_EXPORTER_REQUIRES_P2P_DISTANCE */ + struct pci_dev *exporting_p2p_device; + /* + * These p2p flags are used to support the hard coded + * mechanism for p2p. If an exporting device knows it + * will put MMIO into the sgt then it should set + * exporter_requires_p2p. Importers should set + * importer_accepts_p2p unless it is known that the + * importing HW never supports P2P because of HW issues. + */ + u8 importer_accepts_p2p; + u8 exporter_requires_p2p; + } sgt_data; }; };