diff options
Diffstat (limited to 'arch/x86/kernel/amd_iommu.c')
-rw-r--r-- | arch/x86/kernel/amd_iommu.c | 88 |
1 files changed, 65 insertions, 23 deletions
diff --git a/arch/x86/kernel/amd_iommu.c b/arch/x86/kernel/amd_iommu.c index 9d66b2092ae1..75c7f8c3fe12 100644 --- a/arch/x86/kernel/amd_iommu.c +++ b/arch/x86/kernel/amd_iommu.c | |||
@@ -381,6 +381,39 @@ irqreturn_t amd_iommu_int_handler(int irq, void *data) | |||
381 | * | 381 | * |
382 | ****************************************************************************/ | 382 | ****************************************************************************/ |
383 | 383 | ||
384 | static int wait_on_sem(volatile u64 *sem) | ||
385 | { | ||
386 | int i = 0; | ||
387 | |||
388 | while (*sem == 0 && i < LOOP_TIMEOUT) { | ||
389 | udelay(1); | ||
390 | i += 1; | ||
391 | } | ||
392 | |||
393 | if (i == LOOP_TIMEOUT) { | ||
394 | pr_alert("AMD-Vi: Completion-Wait loop timed out\n"); | ||
395 | return -EIO; | ||
396 | } | ||
397 | |||
398 | return 0; | ||
399 | } | ||
400 | |||
401 | static void copy_cmd_to_buffer(struct amd_iommu *iommu, | ||
402 | struct iommu_cmd *cmd, | ||
403 | u32 tail) | ||
404 | { | ||
405 | u8 *target; | ||
406 | |||
407 | target = iommu->cmd_buf + tail; | ||
408 | tail = (tail + sizeof(*cmd)) % iommu->cmd_buf_size; | ||
409 | |||
410 | /* Copy command to buffer */ | ||
411 | memcpy(target, cmd, sizeof(*cmd)); | ||
412 | |||
413 | /* Tell the IOMMU about it */ | ||
414 | writel(tail, iommu->mmio_base + MMIO_CMD_TAIL_OFFSET); | ||
415 | } | ||
416 | |||
384 | static void build_completion_wait(struct iommu_cmd *cmd, u64 address) | 417 | static void build_completion_wait(struct iommu_cmd *cmd, u64 address) |
385 | { | 418 | { |
386 | WARN_ON(address & 0x7ULL); | 419 | WARN_ON(address & 0x7ULL); |
@@ -432,25 +465,44 @@ static void build_inv_iommu_pages(struct iommu_cmd *cmd, u64 address, | |||
432 | 465 | ||
433 | /* | 466 | /* |
434 | * Writes the command to the IOMMUs command buffer and informs the | 467 | * Writes the command to the IOMMUs command buffer and informs the |
435 | * hardware about the new command. Must be called with iommu->lock held. | 468 | * hardware about the new command. |
436 | */ | 469 | */ |
437 | static int iommu_queue_command(struct amd_iommu *iommu, struct iommu_cmd *cmd) | 470 | static int iommu_queue_command(struct amd_iommu *iommu, struct iommu_cmd *cmd) |
438 | { | 471 | { |
472 | u32 left, tail, head, next_tail; | ||
439 | unsigned long flags; | 473 | unsigned long flags; |
440 | u32 tail, head; | ||
441 | u8 *target; | ||
442 | 474 | ||
443 | WARN_ON(iommu->cmd_buf_size & CMD_BUFFER_UNINITIALIZED); | 475 | WARN_ON(iommu->cmd_buf_size & CMD_BUFFER_UNINITIALIZED); |
476 | |||
477 | again: | ||
444 | spin_lock_irqsave(&iommu->lock, flags); | 478 | spin_lock_irqsave(&iommu->lock, flags); |
445 | tail = readl(iommu->mmio_base + MMIO_CMD_TAIL_OFFSET); | 479 | |
446 | target = iommu->cmd_buf + tail; | 480 | head = readl(iommu->mmio_base + MMIO_CMD_HEAD_OFFSET); |
447 | memcpy_toio(target, cmd, sizeof(*cmd)); | 481 | tail = readl(iommu->mmio_base + MMIO_CMD_TAIL_OFFSET); |
448 | tail = (tail + sizeof(*cmd)) % iommu->cmd_buf_size; | 482 | next_tail = (tail + sizeof(*cmd)) % iommu->cmd_buf_size; |
449 | head = readl(iommu->mmio_base + MMIO_CMD_HEAD_OFFSET); | 483 | left = (head - next_tail) % iommu->cmd_buf_size; |
450 | if (tail == head) | 484 | |
451 | return -ENOMEM; | 485 | if (left <= 2) { |
452 | writel(tail, iommu->mmio_base + MMIO_CMD_TAIL_OFFSET); | 486 | struct iommu_cmd sync_cmd; |
487 | volatile u64 sem = 0; | ||
488 | int ret; | ||
489 | |||
490 | build_completion_wait(&sync_cmd, (u64)&sem); | ||
491 | copy_cmd_to_buffer(iommu, &sync_cmd, tail); | ||
492 | |||
493 | spin_unlock_irqrestore(&iommu->lock, flags); | ||
494 | |||
495 | if ((ret = wait_on_sem(&sem)) != 0) | ||
496 | return ret; | ||
497 | |||
498 | goto again; | ||
499 | } | ||
500 | |||
501 | copy_cmd_to_buffer(iommu, cmd, tail); | ||
502 | |||
503 | /* We need to sync now to make sure all commands are processed */ | ||
453 | iommu->need_sync = true; | 504 | iommu->need_sync = true; |
505 | |||
454 | spin_unlock_irqrestore(&iommu->lock, flags); | 506 | spin_unlock_irqrestore(&iommu->lock, flags); |
455 | 507 | ||
456 | return 0; | 508 | return 0; |
@@ -464,7 +516,7 @@ static int iommu_completion_wait(struct amd_iommu *iommu) | |||
464 | { | 516 | { |
465 | struct iommu_cmd cmd; | 517 | struct iommu_cmd cmd; |
466 | volatile u64 sem = 0; | 518 | volatile u64 sem = 0; |
467 | int ret, i = 0; | 519 | int ret; |
468 | 520 | ||
469 | if (!iommu->need_sync) | 521 | if (!iommu->need_sync) |
470 | return 0; | 522 | return 0; |
@@ -475,17 +527,7 @@ static int iommu_completion_wait(struct amd_iommu *iommu) | |||
475 | if (ret) | 527 | if (ret) |
476 | return ret; | 528 | return ret; |
477 | 529 | ||
478 | while (sem == 0 && i < LOOP_TIMEOUT) { | 530 | return wait_on_sem(&sem); |
479 | udelay(1); | ||
480 | i += 1; | ||
481 | } | ||
482 | |||
483 | if (i == LOOP_TIMEOUT) { | ||
484 | pr_alert("AMD-Vi: Completion-Wait loop timed out\n"); | ||
485 | ret = -EIO; | ||
486 | } | ||
487 | |||
488 | return 0; | ||
489 | } | 531 | } |
490 | 532 | ||
491 | /* | 533 | /* |