summaryrefslogtreecommitdiffstats
path: root/drivers/vfio
diff options
context:
space:
mode:
authorAlexey Kardashevskiy <aik@ozlabs.ru>2015-06-05 02:35:15 -0400
committerMichael Ellerman <mpe@ellerman.id.au>2015-06-11 01:16:49 -0400
commit05c6cfb9dce0d13d37e9d007ee6a4af36f1c0a58 (patch)
tree8bd7df6fe974bb59d2860a1a6725bd44779edaa7 /drivers/vfio
parentc5bb44edee19b2c19221a0b5a68add37ea5733c5 (diff)
powerpc/iommu/powernv: Release replaced TCE
At the moment writing new TCE value to the IOMMU table fails with EBUSY if there is a valid entry already. However PAPR specification allows the guest to write new TCE value without clearing it first. Another problem this patch is addressing is the use of pool locks for external IOMMU users such as VFIO. The pool locks are to protect DMA page allocator rather than entries and since the host kernel does not control what pages are in use, there is no point in pool locks and exchange()+put_page(oldtce) is sufficient to avoid possible races. This adds an exchange() callback to iommu_table_ops which does the same thing as set() plus it returns replaced TCE and DMA direction so the caller can release the pages afterwards. The exchange() receives a physical address unlike set() which receives linear mapping address; and returns a physical address as the clear() does. This implements exchange() for P5IOC2/IODA/IODA2. This adds a requirement for a platform to have exchange() implemented in order to support VFIO. This replaces iommu_tce_build() and iommu_clear_tce() with a single iommu_tce_xchg(). This makes sure that TCE permission bits are not set in TCE passed to IOMMU API as those are to be calculated by platform code from DMA direction. This moves SetPageDirty() to the IOMMU code to make it work for both VFIO ioctl interface in in-kernel TCE acceleration (when it becomes available later). Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru> [aw: for the vfio related changes] Acked-by: Alex Williamson <alex.williamson@redhat.com> Reviewed-by: David Gibson <david@gibson.dropbear.id.au> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Diffstat (limited to 'drivers/vfio')
-rw-r--r--drivers/vfio/vfio_iommu_spapr_tce.c63
1 files changed, 38 insertions, 25 deletions
diff --git a/drivers/vfio/vfio_iommu_spapr_tce.c b/drivers/vfio/vfio_iommu_spapr_tce.c
index 9c720de46c33..a9e2d13c03c0 100644
--- a/drivers/vfio/vfio_iommu_spapr_tce.c
+++ b/drivers/vfio/vfio_iommu_spapr_tce.c
@@ -236,18 +236,11 @@ static void tce_iommu_release(void *iommu_data)
236} 236}
237 237
238static void tce_iommu_unuse_page(struct tce_container *container, 238static void tce_iommu_unuse_page(struct tce_container *container,
239 unsigned long oldtce) 239 unsigned long hpa)
240{ 240{
241 struct page *page; 241 struct page *page;
242 242
243 if (!(oldtce & (TCE_PCI_READ | TCE_PCI_WRITE))) 243 page = pfn_to_page(hpa >> PAGE_SHIFT);
244 return;
245
246 page = pfn_to_page(oldtce >> PAGE_SHIFT);
247
248 if (oldtce & TCE_PCI_WRITE)
249 SetPageDirty(page);
250
251 put_page(page); 244 put_page(page);
252} 245}
253 246
@@ -255,14 +248,21 @@ static int tce_iommu_clear(struct tce_container *container,
255 struct iommu_table *tbl, 248 struct iommu_table *tbl,
256 unsigned long entry, unsigned long pages) 249 unsigned long entry, unsigned long pages)
257{ 250{
258 unsigned long oldtce; 251 unsigned long oldhpa;
252 long ret;
253 enum dma_data_direction direction;
259 254
260 for ( ; pages; --pages, ++entry) { 255 for ( ; pages; --pages, ++entry) {
261 oldtce = iommu_clear_tce(tbl, entry); 256 direction = DMA_NONE;
262 if (!oldtce) 257 oldhpa = 0;
258 ret = iommu_tce_xchg(tbl, entry, &oldhpa, &direction);
259 if (ret)
260 continue;
261
262 if (direction == DMA_NONE)
263 continue; 263 continue;
264 264
265 tce_iommu_unuse_page(container, oldtce); 265 tce_iommu_unuse_page(container, oldhpa);
266 } 266 }
267 267
268 return 0; 268 return 0;
@@ -284,12 +284,13 @@ static int tce_iommu_use_page(unsigned long tce, unsigned long *hpa)
284 284
285static long tce_iommu_build(struct tce_container *container, 285static long tce_iommu_build(struct tce_container *container,
286 struct iommu_table *tbl, 286 struct iommu_table *tbl,
287 unsigned long entry, unsigned long tce, unsigned long pages) 287 unsigned long entry, unsigned long tce, unsigned long pages,
288 enum dma_data_direction direction)
288{ 289{
289 long i, ret = 0; 290 long i, ret = 0;
290 struct page *page; 291 struct page *page;
291 unsigned long hpa; 292 unsigned long hpa;
292 enum dma_data_direction direction = iommu_tce_direction(tce); 293 enum dma_data_direction dirtmp;
293 294
294 for (i = 0; i < pages; ++i) { 295 for (i = 0; i < pages; ++i) {
295 unsigned long offset = tce & IOMMU_PAGE_MASK(tbl) & ~PAGE_MASK; 296 unsigned long offset = tce & IOMMU_PAGE_MASK(tbl) & ~PAGE_MASK;
@@ -305,8 +306,8 @@ static long tce_iommu_build(struct tce_container *container,
305 } 306 }
306 307
307 hpa |= offset; 308 hpa |= offset;
308 ret = iommu_tce_build(tbl, entry + i, (unsigned long) __va(hpa), 309 dirtmp = direction;
309 direction); 310 ret = iommu_tce_xchg(tbl, entry + i, &hpa, &dirtmp);
310 if (ret) { 311 if (ret) {
311 tce_iommu_unuse_page(container, hpa); 312 tce_iommu_unuse_page(container, hpa);
312 pr_err("iommu_tce: %s failed ioba=%lx, tce=%lx, ret=%ld\n", 313 pr_err("iommu_tce: %s failed ioba=%lx, tce=%lx, ret=%ld\n",
@@ -314,6 +315,10 @@ static long tce_iommu_build(struct tce_container *container,
314 tce, ret); 315 tce, ret);
315 break; 316 break;
316 } 317 }
318
319 if (dirtmp != DMA_NONE)
320 tce_iommu_unuse_page(container, hpa);
321
317 tce += IOMMU_PAGE_SIZE(tbl); 322 tce += IOMMU_PAGE_SIZE(tbl);
318 } 323 }
319 324
@@ -378,8 +383,8 @@ static long tce_iommu_ioctl(void *iommu_data,
378 case VFIO_IOMMU_MAP_DMA: { 383 case VFIO_IOMMU_MAP_DMA: {
379 struct vfio_iommu_type1_dma_map param; 384 struct vfio_iommu_type1_dma_map param;
380 struct iommu_table *tbl = NULL; 385 struct iommu_table *tbl = NULL;
381 unsigned long tce;
382 long num; 386 long num;
387 enum dma_data_direction direction;
383 388
384 if (!container->enabled) 389 if (!container->enabled)
385 return -EPERM; 390 return -EPERM;
@@ -405,19 +410,27 @@ static long tce_iommu_ioctl(void *iommu_data,
405 return -EINVAL; 410 return -EINVAL;
406 411
407 /* iova is checked by the IOMMU API */ 412 /* iova is checked by the IOMMU API */
408 tce = param.vaddr; 413 if (param.flags & VFIO_DMA_MAP_FLAG_READ) {
409 if (param.flags & VFIO_DMA_MAP_FLAG_READ) 414 if (param.flags & VFIO_DMA_MAP_FLAG_WRITE)
410 tce |= TCE_PCI_READ; 415 direction = DMA_BIDIRECTIONAL;
411 if (param.flags & VFIO_DMA_MAP_FLAG_WRITE) 416 else
412 tce |= TCE_PCI_WRITE; 417 direction = DMA_TO_DEVICE;
418 } else {
419 if (param.flags & VFIO_DMA_MAP_FLAG_WRITE)
420 direction = DMA_FROM_DEVICE;
421 else
422 return -EINVAL;
423 }
413 424
414 ret = iommu_tce_put_param_check(tbl, param.iova, tce); 425 ret = iommu_tce_put_param_check(tbl, param.iova, param.vaddr);
415 if (ret) 426 if (ret)
416 return ret; 427 return ret;
417 428
418 ret = tce_iommu_build(container, tbl, 429 ret = tce_iommu_build(container, tbl,
419 param.iova >> tbl->it_page_shift, 430 param.iova >> tbl->it_page_shift,
420 tce, param.size >> tbl->it_page_shift); 431 param.vaddr,
432 param.size >> tbl->it_page_shift,
433 direction);
421 434
422 iommu_flush_tce(tbl); 435 iommu_flush_tce(tbl);
423 436