Let the importer tell the exporter which mapping types it supports when it attaches. A new optional dma_buf_attach_ops->supported_mapping_types table lists the importer's DMA_BUF_IMAPPING_*() entries, most preferred first. The entries carry no device so the table is static and shared by all of the importer's attachments; the device passed to dma_buf_dynamic_attach() is handed to the mapping types through the match arguments. Importers that do not provide a table, which includes every dma_buf_attach() caller, support only the SGT type with P2P according to allow_peer2peer, exactly as today.
dma_buf_dynamic_attach() checks if the exporter is mapping aware and calls its ops->match_mapping() function to pick up the exporter match list and call dma_buf_match_mapping(). If unaware it will use dma_buf_sgt_exp_compat_match as a compatibility matchlist that uses the unaware exporter's dma_buf_ops callbacks. The resulting match set is stored in attach->matches with the first entry, the exporter's preferred type, returned by dma_buf_attach_map_type(). In effect dma_buf_attach_map_type() is always available and always makes sense regardless of what combination of aware/unaware importer/exporter is used. The attachment's importer side (dmabuf, importer_ops, importer_priv) is initialised before either callback runs so that exporters can consult it, for example to recognise an importer driven by the same driver. The dma_resv lock is not held at this point, so match_mapping() may only look at properties that cannot change for the lifetime of the attachment. For compatibility with unaware drivers copy the sgt matching data into the attach->dev and peer2peer. If the exporter sets exporter_requires_p2p then only the following are allowed: - importers without a supported_mapping_types table that set importer_ops->allow_peer2peer = true - importers whose supported_mapping_types table includes DMA_BUF_IMAPPING_SGT(DMA_SGT_IMPORTER_ACCEPTS_P2P) Other combinations are blocked. Exporters that want to behave differently based on the importer's capability can declare exporter_requires_p2p=false and check dma_buf_attach_map_type(attach)->sgt_data.importer_accepts_p2p. Or they can declare two SGT exporters with different map/unmap functions. Signed-off-by: Jason Gunthorpe <[email protected]> Signed-off-by: Alistair Popple <[email protected]> --- drivers/dma-buf/dma-buf.c | 62 +++++++++++++++++++++++++++++++++++++++++++---- include/linux/dma-buf.h | 46 +++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 5 deletions(-) diff --git a/drivers/dma-buf/dma-buf.c b/drivers/dma-buf/dma-buf.c index 0dd8d0999a92..1913a84d3363 100644 --- a/drivers/dma-buf/dma-buf.c +++ b/drivers/dma-buf/dma-buf.c @@ -14,6 +14,7 @@ #include <linux/fs.h> #include <linux/slab.h> #include <linux/dma-buf.h> +#include <linux/dma-buf-mapping.h> #include <linux/dma-fence.h> #include <linux/dma-fence-unwrap.h> #include <linux/anon_inodes.h> @@ -714,11 +715,19 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info) int ret; if (WARN_ON(!exp_info->priv || !exp_info->ops - || !exp_info->ops->map_dma_buf - || !exp_info->ops->unmap_dma_buf || !exp_info->ops->release)) return ERR_PTR(-EINVAL); + if (exp_info->ops->match_mapping) { + if (WARN_ON(exp_info->ops->map_dma_buf || + exp_info->ops->unmap_dma_buf)) + return ERR_PTR(-EINVAL); + } else { + if (WARN_ON(!exp_info->ops->map_dma_buf || + !exp_info->ops->unmap_dma_buf)) + return ERR_PTR(-EINVAL); + } + if (WARN_ON(!exp_info->ops->pin != !exp_info->ops->unpin)) return ERR_PTR(-EINVAL); @@ -1027,12 +1036,26 @@ dma_buf_pin_on_map(struct dma_buf_attachment *attach) * Note that this can fail if the backing storage of @dmabuf is in a place not * accessible to @dev, and cannot be moved to a more suitable place. This is * indicated with the error code -EBUSY. + * + * The mapping types the importer supports come from + * &dma_buf_attach_ops.supported_mapping_types. Importers without it, which + * includes every dma_buf_attach() caller, support only the SGT type, with + * P2P according to &dma_buf_attach_ops.allow_peer2peer. This records the + * attachment's match set, i.e. the intersection of types supported by both + * importer and exporter, in &dma_buf_attachment.matches. Later attachments + * by other importers never change the set but may affect what type the + * exporter selects when the attachment is first mapped or pinned. */ struct dma_buf_attachment * dma_buf_dynamic_attach(struct dma_buf *dmabuf, struct device *dev, const struct dma_buf_attach_ops *importer_ops, void *importer_priv) { + struct dma_buf_mapping_match default_match; + struct dma_buf_match_args match_args = { + .dmabuf = dmabuf, + .dev = dev, + }; struct dma_buf_attachment *attach; int ret; @@ -1043,13 +1066,42 @@ dma_buf_dynamic_attach(struct dma_buf *dmabuf, struct device *dev, if (!attach) return ERR_PTR(-ENOMEM); - attach->dev = dev; attach->dmabuf = dmabuf; - if (importer_ops) - attach->peer2peer = importer_ops->allow_peer2peer; attach->importer_ops = importer_ops; attach->importer_priv = importer_priv; + /* + * The exporter's match_mapping() may look at the importer side of the + * attachment, so initialise it before the match. + */ + match_args.attach = attach; + if (importer_ops && importer_ops->supported_mapping_types) { + if (WARN_ON(!importer_ops->nr_supported_mapping_types)) { + ret = -EINVAL; + goto err_attach; + } + match_args.imp_matches = importer_ops->supported_mapping_types; + match_args.imp_len = importer_ops->nr_supported_mapping_types; + } else { + default_match = DMA_BUF_IMAPPING_SGT( + importer_ops && importer_ops->allow_peer2peer ? + DMA_SGT_IMPORTER_ACCEPTS_P2P : + DMA_SGT_NO_P2P); + match_args.imp_matches = &default_match; + match_args.imp_len = 1; + } + + if (dmabuf->ops->match_mapping) { + ret = dmabuf->ops->match_mapping(&match_args); + if (ret) + goto err_attach; + } else { + ret = dma_buf_match_mapping(&match_args, + &dma_buf_sgt_exp_compat_match, 1); + if (ret) + goto err_attach; + } + if (dmabuf->ops->attach) { ret = dmabuf->ops->attach(dmabuf, attach); if (ret) diff --git a/include/linux/dma-buf.h b/include/linux/dma-buf.h index de73f931a2f8..ecfd9b2d2aac 100644 --- a/include/linux/dma-buf.h +++ b/include/linux/dma-buf.h @@ -29,6 +29,7 @@ struct dma_buf; struct dma_buf_attachment; struct dma_buf_mapping_type; struct dma_buf_mapping_exp_ops; +struct dma_buf_match_args; /* @@ -316,6 +317,27 @@ struct dma_buf_ops { int (*vmap)(struct dma_buf *dmabuf, struct iosys_map *map); void (*vunmap)(struct dma_buf *dmabuf, struct iosys_map *map); + + /** + * @match_mapping: + * + * Called during attach, without the dma_resv lock held. Allows the + * exporter to build its own exporter struct dma_buf_mapping_match[] + * and call dma_buf_match_mapping() to record every mutually + * acceptable type in &dma_buf_attachment.matches in the exporter's + * order of preference. + * + * The match set is fixed for the lifetime of the attachment, so every + * input to the decision must hold for that long: either static for the + * dma_buf (the importer's list, PCI P2P topology, buffer creation + * flags) or runtime state the exporter pins until @detach, for example + * a runtime PM reference. + * + * It must not depend on the set of attachments or on current buffer + * placement as matches are static and buffer placement can change at + * any time until the final location is resolved during map/pin. + */ + int (*match_mapping)(struct dma_buf_match_args *args); }; /** @@ -499,6 +521,30 @@ struct dma_buf_attach_ops { * required behavior. */ void (*invalidate_mappings)(struct dma_buf_attachment *attach); + + /** + * @supported_mapping_types: [optional] mapping types the importer + * can use + * + * Array of @nr_supported_mapping_types DMA_BUF_IMAPPING_*() entries + * the importer can use in priority order. Entries are static so can be + * shared by all of the importer's attachments. Any required dynamic + * information, such as the importing device, is passed to each type's + * callbacks by dma_buf_dynamic_attach() for recording in the + * negotiated entry. + * + * If not provided the importer supports only the SGT mapping type, + * accepting P2P according to @allow_peer2peer. When provided an SGT + * entry's DMA_SGT_IMPORTER_ACCEPTS_P2P flag takes the place of + * @allow_peer2peer. + */ + const struct dma_buf_mapping_match *supported_mapping_types; + + /** + * @nr_supported_mapping_types: number of entries in + * @supported_mapping_types + */ + unsigned int nr_supported_mapping_types; }; /** -- 2.54.0
