|
15 | 15 | #include <linux/err.h> |
16 | 16 | #include <linux/slab.h> |
17 | 17 | #include <linux/errno.h> |
| 18 | +#include <uapi/linux/iommufd.h> |
18 | 19 |
|
19 | 20 | #include "io_pagetable.h" |
20 | 21 | #include "double_span.h" |
@@ -412,6 +413,118 @@ int iopt_map_user_pages(struct iommufd_ctx *ictx, struct io_pagetable *iopt, |
412 | 413 | return 0; |
413 | 414 | } |
414 | 415 |
|
| 416 | +struct iova_bitmap_fn_arg { |
| 417 | + struct io_pagetable *iopt; |
| 418 | + struct iommu_domain *domain; |
| 419 | + struct iommu_dirty_bitmap *dirty; |
| 420 | +}; |
| 421 | + |
| 422 | +static int __iommu_read_and_clear_dirty(struct iova_bitmap *bitmap, |
| 423 | + unsigned long iova, size_t length, |
| 424 | + void *opaque) |
| 425 | +{ |
| 426 | + struct iopt_area *area; |
| 427 | + struct iopt_area_contig_iter iter; |
| 428 | + struct iova_bitmap_fn_arg *arg = opaque; |
| 429 | + struct iommu_domain *domain = arg->domain; |
| 430 | + struct iommu_dirty_bitmap *dirty = arg->dirty; |
| 431 | + const struct iommu_dirty_ops *ops = domain->dirty_ops; |
| 432 | + unsigned long last_iova = iova + length - 1; |
| 433 | + int ret; |
| 434 | + |
| 435 | + iopt_for_each_contig_area(&iter, area, arg->iopt, iova, last_iova) { |
| 436 | + unsigned long last = min(last_iova, iopt_area_last_iova(area)); |
| 437 | + |
| 438 | + ret = ops->read_and_clear_dirty(domain, iter.cur_iova, |
| 439 | + last - iter.cur_iova + 1, 0, |
| 440 | + dirty); |
| 441 | + if (ret) |
| 442 | + return ret; |
| 443 | + } |
| 444 | + |
| 445 | + if (!iopt_area_contig_done(&iter)) |
| 446 | + return -EINVAL; |
| 447 | + return 0; |
| 448 | +} |
| 449 | + |
| 450 | +static int |
| 451 | +iommu_read_and_clear_dirty(struct iommu_domain *domain, |
| 452 | + struct io_pagetable *iopt, unsigned long flags, |
| 453 | + struct iommu_hwpt_get_dirty_bitmap *bitmap) |
| 454 | +{ |
| 455 | + const struct iommu_dirty_ops *ops = domain->dirty_ops; |
| 456 | + struct iommu_iotlb_gather gather; |
| 457 | + struct iommu_dirty_bitmap dirty; |
| 458 | + struct iova_bitmap_fn_arg arg; |
| 459 | + struct iova_bitmap *iter; |
| 460 | + int ret = 0; |
| 461 | + |
| 462 | + if (!ops || !ops->read_and_clear_dirty) |
| 463 | + return -EOPNOTSUPP; |
| 464 | + |
| 465 | + iter = iova_bitmap_alloc(bitmap->iova, bitmap->length, |
| 466 | + bitmap->page_size, |
| 467 | + u64_to_user_ptr(bitmap->data)); |
| 468 | + if (IS_ERR(iter)) |
| 469 | + return -ENOMEM; |
| 470 | + |
| 471 | + iommu_dirty_bitmap_init(&dirty, iter, &gather); |
| 472 | + |
| 473 | + arg.iopt = iopt; |
| 474 | + arg.domain = domain; |
| 475 | + arg.dirty = &dirty; |
| 476 | + iova_bitmap_for_each(iter, &arg, __iommu_read_and_clear_dirty); |
| 477 | + |
| 478 | + iommu_iotlb_sync(domain, &gather); |
| 479 | + iova_bitmap_free(iter); |
| 480 | + |
| 481 | + return ret; |
| 482 | +} |
| 483 | + |
| 484 | +int iommufd_check_iova_range(struct io_pagetable *iopt, |
| 485 | + struct iommu_hwpt_get_dirty_bitmap *bitmap) |
| 486 | +{ |
| 487 | + size_t iommu_pgsize = iopt->iova_alignment; |
| 488 | + u64 last_iova; |
| 489 | + |
| 490 | + if (check_add_overflow(bitmap->iova, bitmap->length - 1, &last_iova)) |
| 491 | + return -EOVERFLOW; |
| 492 | + |
| 493 | + if (bitmap->iova > ULONG_MAX || last_iova > ULONG_MAX) |
| 494 | + return -EOVERFLOW; |
| 495 | + |
| 496 | + if ((bitmap->iova & (iommu_pgsize - 1)) || |
| 497 | + ((last_iova + 1) & (iommu_pgsize - 1))) |
| 498 | + return -EINVAL; |
| 499 | + |
| 500 | + if (!bitmap->page_size) |
| 501 | + return -EINVAL; |
| 502 | + |
| 503 | + if ((bitmap->iova & (bitmap->page_size - 1)) || |
| 504 | + ((last_iova + 1) & (bitmap->page_size - 1))) |
| 505 | + return -EINVAL; |
| 506 | + |
| 507 | + return 0; |
| 508 | +} |
| 509 | + |
| 510 | +int iopt_read_and_clear_dirty_data(struct io_pagetable *iopt, |
| 511 | + struct iommu_domain *domain, |
| 512 | + unsigned long flags, |
| 513 | + struct iommu_hwpt_get_dirty_bitmap *bitmap) |
| 514 | +{ |
| 515 | + int ret; |
| 516 | + |
| 517 | + ret = iommufd_check_iova_range(iopt, bitmap); |
| 518 | + if (ret) |
| 519 | + return ret; |
| 520 | + |
| 521 | + down_read(&iopt->iova_rwsem); |
| 522 | + ret = iommu_read_and_clear_dirty(domain, iopt, flags, bitmap); |
| 523 | + up_read(&iopt->iova_rwsem); |
| 524 | + |
| 525 | + return ret; |
| 526 | +} |
| 527 | + |
415 | 528 | static int iopt_clear_dirty_data(struct io_pagetable *iopt, |
416 | 529 | struct iommu_domain *domain) |
417 | 530 | { |
|
0 commit comments