File Index
Files and directories
Sign in
FILE

vfio_pci_dmabuf.c

File preview with syntax highlighting.

linux-6.19.14/drivers/vfio/pci/vfio_pci_dmabuf.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/* Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES.
  3 */
  4#include <linux/dma-buf-mapping.h>
  5#include <linux/pci-p2pdma.h>
  6#include <linux/dma-resv.h>
  7
  8#include "vfio_pci_priv.h"
  9
 10MODULE_IMPORT_NS("DMA_BUF");
 11
 12struct vfio_pci_dma_buf {
 13	struct dma_buf *dmabuf;
 14	struct vfio_pci_core_device *vdev;
 15	struct list_head dmabufs_elm;
 16	size_t size;
 17	struct dma_buf_phys_vec *phys_vec;
 18	struct p2pdma_provider *provider;
 19	u32 nr_ranges;
 20	u8 revoked : 1;
 21};
 22
 23static int vfio_pci_dma_buf_pin(struct dma_buf_attachment *attachment)
 24{
 25	return -EOPNOTSUPP;
 26}
 27
 28static void vfio_pci_dma_buf_unpin(struct dma_buf_attachment *attachment)
 29{
 30	/* Do nothing */
 31}
 32
 33static int vfio_pci_dma_buf_attach(struct dma_buf *dmabuf,
 34				   struct dma_buf_attachment *attachment)
 35{
 36	struct vfio_pci_dma_buf *priv = dmabuf->priv;
 37
 38	if (!attachment->peer2peer)
 39		return -EOPNOTSUPP;
 40
 41	if (priv->revoked)
 42		return -ENODEV;
 43
 44	return 0;
 45}
 46
 47static struct sg_table *
 48vfio_pci_dma_buf_map(struct dma_buf_attachment *attachment,
 49		     enum dma_data_direction dir)
 50{
 51	struct vfio_pci_dma_buf *priv = attachment->dmabuf->priv;
 52
 53	dma_resv_assert_held(priv->dmabuf->resv);
 54
 55	if (priv->revoked)
 56		return ERR_PTR(-ENODEV);
 57
 58	return dma_buf_phys_vec_to_sgt(attachment, priv->provider,
 59				       priv->phys_vec, priv->nr_ranges,
 60				       priv->size, dir);
 61}
 62
 63static void vfio_pci_dma_buf_unmap(struct dma_buf_attachment *attachment,
 64				   struct sg_table *sgt,
 65				   enum dma_data_direction dir)
 66{
 67	dma_buf_free_sgt(attachment, sgt, dir);
 68}
 69
 70static void vfio_pci_dma_buf_release(struct dma_buf *dmabuf)
 71{
 72	struct vfio_pci_dma_buf *priv = dmabuf->priv;
 73
 74	/*
 75	 * Either this or vfio_pci_dma_buf_cleanup() will remove from the list.
 76	 * The refcount prevents both.
 77	 */
 78	if (priv->vdev) {
 79		down_write(&priv->vdev->memory_lock);
 80		list_del_init(&priv->dmabufs_elm);
 81		up_write(&priv->vdev->memory_lock);
 82		vfio_device_put_registration(&priv->vdev->vdev);
 83	}
 84	kfree(priv->phys_vec);
 85	kfree(priv);
 86}
 87
 88static const struct dma_buf_ops vfio_pci_dmabuf_ops = {
 89	.pin = vfio_pci_dma_buf_pin,
 90	.unpin = vfio_pci_dma_buf_unpin,
 91	.attach = vfio_pci_dma_buf_attach,
 92	.map_dma_buf = vfio_pci_dma_buf_map,
 93	.unmap_dma_buf = vfio_pci_dma_buf_unmap,
 94	.release = vfio_pci_dma_buf_release,
 95};
 96
 97/*
 98 * This is a temporary "private interconnect" between VFIO DMABUF and iommufd.
 99 * It allows the two co-operating drivers to exchange the physical address of
100 * the BAR. This is to be replaced with a formal DMABUF system for negotiated
101 * interconnect types.
102 *
103 * If this function succeeds the following are true:
104 *  - There is one physical range and it is pointing to MMIO
105 *  - When move_notify is called it means revoke, not move, vfio_dma_buf_map
106 *    will fail if it is currently revoked
107 */
108int vfio_pci_dma_buf_iommufd_map(struct dma_buf_attachment *attachment,
109				 struct dma_buf_phys_vec *phys)
110{
111	struct vfio_pci_dma_buf *priv;
112
113	dma_resv_assert_held(attachment->dmabuf->resv);
114
115	if (attachment->dmabuf->ops != &vfio_pci_dmabuf_ops)
116		return -EOPNOTSUPP;
117
118	priv = attachment->dmabuf->priv;
119	if (priv->revoked)
120		return -ENODEV;
121
122	/* More than one range to iommufd will require proper DMABUF support */
123	if (priv->nr_ranges != 1)
124		return -EOPNOTSUPP;
125
126	*phys = priv->phys_vec[0];
127	return 0;
128}
129EXPORT_SYMBOL_FOR_MODULES(vfio_pci_dma_buf_iommufd_map, "iommufd");
130
131int vfio_pci_core_fill_phys_vec(struct dma_buf_phys_vec *phys_vec,
132				struct vfio_region_dma_range *dma_ranges,
133				size_t nr_ranges, phys_addr_t start,
134				phys_addr_t len)
135{
136	phys_addr_t max_addr;
137	unsigned int i;
138
139	max_addr = start + len;
140	for (i = 0; i < nr_ranges; i++) {
141		phys_addr_t end;
142
143		if (!dma_ranges[i].length)
144			return -EINVAL;
145
146		if (check_add_overflow(start, dma_ranges[i].offset,
147				       &phys_vec[i].paddr) ||
148		    check_add_overflow(phys_vec[i].paddr,
149				       dma_ranges[i].length, &end))
150			return -EOVERFLOW;
151		if (end > max_addr)
152			return -EINVAL;
153
154		phys_vec[i].len = dma_ranges[i].length;
155	}
156	return 0;
157}
158EXPORT_SYMBOL_GPL(vfio_pci_core_fill_phys_vec);
159
160int vfio_pci_core_get_dmabuf_phys(struct vfio_pci_core_device *vdev,
161				  struct p2pdma_provider **provider,
162				  unsigned int region_index,
163				  struct dma_buf_phys_vec *phys_vec,
164				  struct vfio_region_dma_range *dma_ranges,
165				  size_t nr_ranges)
166{
167	struct pci_dev *pdev = vdev->pdev;
168
169	*provider = pcim_p2pdma_provider(pdev, region_index);
170	if (!*provider)
171		return -EINVAL;
172
173	return vfio_pci_core_fill_phys_vec(
174		phys_vec, dma_ranges, nr_ranges,
175		pci_resource_start(pdev, region_index),
176		pci_resource_len(pdev, region_index));
177}
178EXPORT_SYMBOL_GPL(vfio_pci_core_get_dmabuf_phys);
179
180static int validate_dmabuf_input(struct vfio_device_feature_dma_buf *dma_buf,
181				 struct vfio_region_dma_range *dma_ranges,
182				 size_t *lengthp)
183{
184	size_t length = 0;
185	u32 i;
186
187	for (i = 0; i < dma_buf->nr_ranges; i++) {
188		u64 offset = dma_ranges[i].offset;
189		u64 len = dma_ranges[i].length;
190
191		if (!len || !PAGE_ALIGNED(offset) || !PAGE_ALIGNED(len))
192			return -EINVAL;
193
194		if (check_add_overflow(length, len, &length))
195			return -EINVAL;
196	}
197
198	/*
199	 * dma_iova_try_alloc() will WARN on if userspace proposes a size that
200	 * is too big, eg with lots of ranges.
201	 */
202	if ((u64)(length) & DMA_IOVA_USE_SWIOTLB)
203		return -EINVAL;
204
205	*lengthp = length;
206	return 0;
207}
208
209int vfio_pci_core_feature_dma_buf(struct vfio_pci_core_device *vdev, u32 flags,
210				  struct vfio_device_feature_dma_buf __user *arg,
211				  size_t argsz)
212{
213	struct vfio_device_feature_dma_buf get_dma_buf = {};
214	struct vfio_region_dma_range *dma_ranges;
215	DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
216	struct vfio_pci_dma_buf *priv;
217	size_t length;
218	int ret;
219
220	if (!vdev->pci_ops || !vdev->pci_ops->get_dmabuf_phys)
221		return -EOPNOTSUPP;
222
223	ret = vfio_check_feature(flags, argsz, VFIO_DEVICE_FEATURE_GET,
224				 sizeof(get_dma_buf));
225	if (ret != 1)
226		return ret;
227
228	if (copy_from_user(&get_dma_buf, arg, sizeof(get_dma_buf)))
229		return -EFAULT;
230
231	if (!get_dma_buf.nr_ranges || get_dma_buf.flags)
232		return -EINVAL;
233
234	/*
235	 * For PCI the region_index is the BAR number like everything else.
236	 */
237	if (get_dma_buf.region_index >= VFIO_PCI_ROM_REGION_INDEX)
238		return -ENODEV;
239
240	dma_ranges = memdup_array_user(&arg->dma_ranges, get_dma_buf.nr_ranges,
241				       sizeof(*dma_ranges));
242	if (IS_ERR(dma_ranges))
243		return PTR_ERR(dma_ranges);
244
245	ret = validate_dmabuf_input(&get_dma_buf, dma_ranges, &length);
246	if (ret)
247		goto err_free_ranges;
248
249	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
250	if (!priv) {
251		ret = -ENOMEM;
252		goto err_free_ranges;
253	}
254	priv->phys_vec = kcalloc(get_dma_buf.nr_ranges, sizeof(*priv->phys_vec),
255				 GFP_KERNEL);
256	if (!priv->phys_vec) {
257		ret = -ENOMEM;
258		goto err_free_priv;
259	}
260
261	priv->vdev = vdev;
262	priv->nr_ranges = get_dma_buf.nr_ranges;
263	priv->size = length;
264	ret = vdev->pci_ops->get_dmabuf_phys(vdev, &priv->provider,
265					     get_dma_buf.region_index,
266					     priv->phys_vec, dma_ranges,
267					     priv->nr_ranges);
268	if (ret)
269		goto err_free_phys;
270
271	kfree(dma_ranges);
272	dma_ranges = NULL;
273
274	if (!vfio_device_try_get_registration(&vdev->vdev)) {
275		ret = -ENODEV;
276		goto err_free_phys;
277	}
278
279	exp_info.ops = &vfio_pci_dmabuf_ops;
280	exp_info.size = priv->size;
281	exp_info.flags = get_dma_buf.open_flags;
282	exp_info.priv = priv;
283
284	priv->dmabuf = dma_buf_export(&exp_info);
285	if (IS_ERR(priv->dmabuf)) {
286		ret = PTR_ERR(priv->dmabuf);
287		goto err_dev_put;
288	}
289
290	/* dma_buf_put() now frees priv */
291	INIT_LIST_HEAD(&priv->dmabufs_elm);
292	down_write(&vdev->memory_lock);
293	dma_resv_lock(priv->dmabuf->resv, NULL);
294	priv->revoked = !__vfio_pci_memory_enabled(vdev);
295	list_add_tail(&priv->dmabufs_elm, &vdev->dmabufs);
296	dma_resv_unlock(priv->dmabuf->resv);
297	up_write(&vdev->memory_lock);
298
299	/*
300	 * dma_buf_fd() consumes the reference, when the file closes the dmabuf
301	 * will be released.
302	 */
303	ret = dma_buf_fd(priv->dmabuf, get_dma_buf.open_flags);
304	if (ret < 0)
305		dma_buf_put(priv->dmabuf);
306
307	return ret;
308
309err_dev_put:
310	vfio_device_put_registration(&vdev->vdev);
311err_free_phys:
312	kfree(priv->phys_vec);
313err_free_priv:
314	kfree(priv);
315err_free_ranges:
316	kfree(dma_ranges);
317	return ret;
318}
319
320void vfio_pci_dma_buf_move(struct vfio_pci_core_device *vdev, bool revoked)
321{
322	struct vfio_pci_dma_buf *priv;
323	struct vfio_pci_dma_buf *tmp;
324
325	lockdep_assert_held_write(&vdev->memory_lock);
326
327	list_for_each_entry_safe(priv, tmp, &vdev->dmabufs, dmabufs_elm) {
328		if (!get_file_active(&priv->dmabuf->file))
329			continue;
330
331		if (priv->revoked != revoked) {
332			dma_resv_lock(priv->dmabuf->resv, NULL);
333			priv->revoked = revoked;
334			dma_buf_move_notify(priv->dmabuf);
335			dma_resv_unlock(priv->dmabuf->resv);
336		}
337		fput(priv->dmabuf->file);
338	}
339}
340
341void vfio_pci_dma_buf_cleanup(struct vfio_pci_core_device *vdev)
342{
343	struct vfio_pci_dma_buf *priv;
344	struct vfio_pci_dma_buf *tmp;
345
346	down_write(&vdev->memory_lock);
347	list_for_each_entry_safe(priv, tmp, &vdev->dmabufs, dmabufs_elm) {
348		if (!get_file_active(&priv->dmabuf->file))
349			continue;
350
351		dma_resv_lock(priv->dmabuf->resv, NULL);
352		list_del_init(&priv->dmabufs_elm);
353		priv->vdev = NULL;
354		priv->revoked = true;
355		dma_buf_move_notify(priv->dmabuf);
356		dma_resv_unlock(priv->dmabuf->resv);
357		vfio_device_put_registration(&vdev->vdev);
358		fput(priv->dmabuf->file);
359	}
360	up_write(&vdev->memory_lock);
361}
~/ Read-only file preview.