]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge tag 'iommu-updates-v4.2' of git://git.kernel.org/pub/scm/linux/kernel/git/joro...
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 24 Jun 2015 01:27:19 +0000 (18:27 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 24 Jun 2015 01:27:19 +0000 (18:27 -0700)
Pull IOMMU updates from Joerg Roedel:
 "This time with bigger changes than usual:

   - A new IOMMU driver for the ARM SMMUv3.

     This IOMMU is pretty different from SMMUv1 and v2 in that it is
     configured through in-memory structures and not through the MMIO
     register region.  The ARM SMMUv3 also supports IO demand paging for
     PCI devices with PRI/PASID capabilities, but this is not
     implemented in the driver yet.

   - Lots of cleanups and device-tree support for the Exynos IOMMU
     driver.  This is part of the effort to bring Exynos DRM support
     upstream.

   - Introduction of default domains into the IOMMU core code.

     The rationale behind this is to move functionalily out of the IOMMU
     drivers to common code to get to a unified behavior between
     different drivers.  The patches here introduce a default domain for
     iommu-groups (isolation groups).

     A device will now always be attached to a domain, either the
     default domain or another domain handled by the device driver.  The
     IOMMU drivers have to be modified to make use of that feature.  So
     long the AMD IOMMU driver is converted, with others to follow.

   - Patches for the Intel VT-d drvier to fix DMAR faults that happen
     when a kdump kernel boots.

     When the kdump kernel boots it re-initializes the IOMMU hardware,
     which destroys all mappings from the crashed kernel.  As this
     happens before the endpoint devices are re-initialized, any
     in-flight DMA causes a DMAR fault.  These faults cause PCI master
     aborts, which some devices can't handle properly and go into an
     undefined state, so that the device driver in the kdump kernel
     fails to initialize them and the dump fails.

     This is now fixed by copying over the mapping structures (only
     context tables and interrupt remapping tables) from the old kernel
     and keep the old mappings in place until the device driver of the
     new kernel takes over.  This emulates the the behavior without an
     IOMMU to the best degree possible.

   - A couple of other small fixes and cleanups"

* tag 'iommu-updates-v4.2' of git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu: (69 commits)
  iommu/amd: Handle large pages correctly in free_pagetable
  iommu/vt-d: Don't disable IR when it was previously enabled
  iommu/vt-d: Make sure copied over IR entries are not reused
  iommu/vt-d: Copy IR table from old kernel when in kdump mode
  iommu/vt-d: Set IRTA in intel_setup_irq_remapping
  iommu/vt-d: Disable IRQ remapping in intel_prepare_irq_remapping
  iommu/vt-d: Move QI initializationt to intel_setup_irq_remapping
  iommu/vt-d: Move EIM detection to intel_prepare_irq_remapping
  iommu/vt-d: Enable Translation only if it was previously disabled
  iommu/vt-d: Don't disable translation prior to OS handover
  iommu/vt-d: Don't copy translation tables if RTT bit needs to be changed
  iommu/vt-d: Don't do early domain assignment if kdump kernel
  iommu/vt-d: Allocate si_domain in init_dmars()
  iommu/vt-d: Mark copied context entries
  iommu/vt-d: Do not re-use domain-ids from the old kernel
  iommu/vt-d: Copy translation tables from old kernel
  iommu/vt-d: Detect pre enabled translation
  iommu/vt-d: Make root entry visible for hardware right after allocation
  iommu/vt-d: Init QI before root entry is allocated
  iommu/vt-d: Cleanup log messages
  ...

1  2 
MAINTAINERS
drivers/iommu/amd_iommu.c
drivers/iommu/amd_iommu_init.c
drivers/iommu/amd_iommu_proto.h
drivers/iommu/amd_iommu_types.h
drivers/iommu/dmar.c
drivers/iommu/intel_irq_remapping.c
include/linux/intel-iommu.h

diff --cc MAINTAINERS
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
index 536f2d8ea41abe502a88628ffc54cc61db6a6ff7,c5886582b64fbb629d0cdacfcb96ba41443d3985..c9db04d4ef39ae36553279859b6ca0f1c7972db1
@@@ -1642,17 -1642,26 +1642,17 @@@ int dmar_set_interrupt(struct intel_iom
        if (iommu->irq)
                return 0;
  
 -      irq = dmar_alloc_hwirq();
 -      if (irq <= 0) {
 +      irq = dmar_alloc_hwirq(iommu->seq_id, iommu->node, iommu);
 +      if (irq > 0) {
 +              iommu->irq = irq;
 +      } else {
-               pr_err("IOMMU: no free vectors\n");
+               pr_err("No free IRQ vectors\n");
                return -EINVAL;
        }
  
 -      irq_set_handler_data(irq, iommu);
 -      iommu->irq = irq;
 -
 -      ret = arch_setup_dmar_msi(irq);
 -      if (ret) {
 -              irq_set_handler_data(irq, NULL);
 -              iommu->irq = 0;
 -              dmar_free_hwirq(irq);
 -              return ret;
 -      }
 -
        ret = request_irq(irq, dmar_fault, IRQF_NO_THREAD, iommu->name, iommu);
        if (ret)
-               pr_err("IOMMU: can't request irq\n");
+               pr_err("Can't request irq\n");
        return ret;
  }
  
index 80f1d1486247f97959d0a8bc58dda6f54d8fb202,47fcebf39e9e26717de9824734c4b15d3acc4f2e..f15692a410c7e7064e844be2b7d83feee20ef5c5
@@@ -8,7 -11,7 +11,8 @@@
  #include <linux/irq.h>
  #include <linux/intel-iommu.h>
  #include <linux/acpi.h>
 +#include <linux/irqdomain.h>
+ #include <linux/crash_dump.h>
  #include <asm/io_apic.h>
  #include <asm/smp.h>
  #include <asm/cpu.h>
@@@ -72,14 -54,63 +76,34 @@@ static struct hpet_scope ir_hpet[MAX_HP
   * the dmar_global_lock.
   */
  static DEFINE_RAW_SPINLOCK(irq_2_ir_lock);
 +static struct irq_domain_ops intel_ir_domain_ops;
  
+ static void iommu_disable_irq_remapping(struct intel_iommu *iommu);
  static int __init parse_ioapics_under_ir(void);
  
 -static struct irq_2_iommu *irq_2_iommu(unsigned int irq)
 -{
 -      struct irq_cfg *cfg = irq_cfg(irq);
 -      return cfg ? &cfg->irq_2_iommu : NULL;
 -}
 -
 -static int get_irte(int irq, struct irte *entry)
 -{
 -      struct irq_2_iommu *irq_iommu = irq_2_iommu(irq);
 -      unsigned long flags;
 -      int index;
 -
 -      if (!entry || !irq_iommu)
 -              return -1;
 -
 -      raw_spin_lock_irqsave(&irq_2_ir_lock, flags);
 -
 -      if (unlikely(!irq_iommu->iommu)) {
 -              raw_spin_unlock_irqrestore(&irq_2_ir_lock, flags);
 -              return -1;
 -      }
 -
 -      index = irq_iommu->irte_index + irq_iommu->sub_handle;
 -      *entry = *(irq_iommu->iommu->ir_table->base + index);
 -
 -      raw_spin_unlock_irqrestore(&irq_2_ir_lock, flags);
 -      return 0;
 -}
 -
 -static int alloc_irte(struct intel_iommu *iommu, int irq, u16 count)
+ static bool ir_pre_enabled(struct intel_iommu *iommu)
+ {
+       return (iommu->flags & VTD_FLAG_IRQ_REMAP_PRE_ENABLED);
+ }
+ static void clear_ir_pre_enabled(struct intel_iommu *iommu)
+ {
+       iommu->flags &= ~VTD_FLAG_IRQ_REMAP_PRE_ENABLED;
+ }
+ static void init_ir_status(struct intel_iommu *iommu)
+ {
+       u32 gsts;
+       gsts = readl(iommu->reg + DMAR_GSTS_REG);
+       if (gsts & DMA_GSTS_IRES)
+               iommu->flags |= VTD_FLAG_IRQ_REMAP_PRE_ENABLED;
+ }
 +static int alloc_irte(struct intel_iommu *iommu, int irq,
 +                    struct irq_2_iommu *irq_iommu, u16 count)
  {
        struct ir_table *table = iommu->ir_table;
 -      struct irq_2_iommu *irq_iommu = irq_2_iommu(irq);
 -      struct irq_cfg *cfg = irq_cfg(irq);
        unsigned int mask = 0;
        unsigned long flags;
        int index;
@@@ -676,11 -784,16 +756,11 @@@ static int __init intel_enable_irq_rema
  
        irq_remapping_enabled = 1;
  
 -      /*
 -       * VT-d has a different layout for IO-APIC entries when
 -       * interrupt remapping is enabled. So it needs a special routine
 -       * to print IO-APIC entries for debugging purposes too.
 -       */
 -      x86_io_apic_ops.print_entries = intel_ir_io_apic_print_entries;
 +      set_irq_posting_cap();
  
-       pr_info("Enabled IRQ remapping in %s mode\n", eim ? "x2apic" : "xapic");
+       pr_info("Enabled IRQ remapping in %s mode\n", eim_mode ? "x2apic" : "xapic");
  
-       return eim ? IRQ_REMAP_X2APIC_MODE : IRQ_REMAP_XAPIC_MODE;
+       return eim_mode ? IRQ_REMAP_X2APIC_MODE : IRQ_REMAP_XAPIC_MODE;
  
  error:
        intel_cleanup_irq_remapping();
@@@ -1043,220 -1176,92 +1124,219 @@@ intel_ir_set_affinity(struct irq_data *
         * at the new destination. So, time to cleanup the previous
         * vector allocation.
         */
 -      if (cfg->move_in_progress)
 -              send_cleanup_vector(cfg);
 +      send_cleanup_vector(cfg);
  
 -      cpumask_copy(data->affinity, mask);
 -      return 0;
 +      return IRQ_SET_MASK_OK_DONE;
  }
  
 -static void intel_compose_msi_msg(struct pci_dev *pdev,
 -                                unsigned int irq, unsigned int dest,
 -                                struct msi_msg *msg, u8 hpet_id)
 +static void intel_ir_compose_msi_msg(struct irq_data *irq_data,
 +                                   struct msi_msg *msg)
  {
 -      struct irq_cfg *cfg;
 -      struct irte irte;
 -      u16 sub_handle = 0;
 -      int ir_index;
 -
 -      cfg = irq_cfg(irq);
 +      struct intel_ir_data *ir_data = irq_data->chip_data;
  
 -      ir_index = map_irq_to_irte_handle(irq, &sub_handle);
 -      BUG_ON(ir_index == -1);
 +      *msg = ir_data->msi_entry;
 +}
  
 -      prepare_irte(&irte, cfg->vector, dest);
 +static int intel_ir_set_vcpu_affinity(struct irq_data *data, void *info)
 +{
 +      struct intel_ir_data *ir_data = data->chip_data;
 +      struct vcpu_data *vcpu_pi_info = info;
  
 -      /* Set source-id of interrupt request */
 -      if (pdev)
 -              set_msi_sid(&irte, pdev);
 -      else
 -              set_hpet_sid(&irte, hpet_id);
 +      /* stop posting interrupts, back to remapping mode */
 +      if (!vcpu_pi_info) {
 +              modify_irte(&ir_data->irq_2_iommu, &ir_data->irte_entry);
 +      } else {
 +              struct irte irte_pi;
  
 -      modify_irte(irq, &irte);
 +              /*
 +               * We are not caching the posted interrupt entry. We
 +               * copy the data from the remapped entry and modify
 +               * the fields which are relevant for posted mode. The
 +               * cached remapped entry is used for switching back to
 +               * remapped mode.
 +               */
 +              memset(&irte_pi, 0, sizeof(irte_pi));
 +              dmar_copy_shared_irte(&irte_pi, &ir_data->irte_entry);
 +
 +              /* Update the posted mode fields */
 +              irte_pi.p_pst = 1;
 +              irte_pi.p_urgent = 0;
 +              irte_pi.p_vector = vcpu_pi_info->vector;
 +              irte_pi.pda_l = (vcpu_pi_info->pi_desc_addr >>
 +                              (32 - PDA_LOW_BIT)) & ~(-1UL << PDA_LOW_BIT);
 +              irte_pi.pda_h = (vcpu_pi_info->pi_desc_addr >> 32) &
 +                              ~(-1UL << PDA_HIGH_BIT);
 +
 +              modify_irte(&ir_data->irq_2_iommu, &irte_pi);
 +      }
  
 -      msg->address_hi = MSI_ADDR_BASE_HI;
 -      msg->data = sub_handle;
 -      msg->address_lo = MSI_ADDR_BASE_LO | MSI_ADDR_IR_EXT_INT |
 -                        MSI_ADDR_IR_SHV |
 -                        MSI_ADDR_IR_INDEX1(ir_index) |
 -                        MSI_ADDR_IR_INDEX2(ir_index);
 +      return 0;
  }
  
 -/*
 - * Map the PCI dev to the corresponding remapping hardware unit
 - * and allocate 'nvec' consecutive interrupt-remapping table entries
 - * in it.
 - */
 -static int intel_msi_alloc_irq(struct pci_dev *dev, int irq, int nvec)
 +static struct irq_chip intel_ir_chip = {
 +      .irq_ack = ir_ack_apic_edge,
 +      .irq_set_affinity = intel_ir_set_affinity,
 +      .irq_compose_msi_msg = intel_ir_compose_msi_msg,
 +      .irq_set_vcpu_affinity = intel_ir_set_vcpu_affinity,
 +};
 +
 +static void intel_irq_remapping_prepare_irte(struct intel_ir_data *data,
 +                                           struct irq_cfg *irq_cfg,
 +                                           struct irq_alloc_info *info,
 +                                           int index, int sub_handle)
  {
 -      struct intel_iommu *iommu;
 -      int index;
 +      struct IR_IO_APIC_route_entry *entry;
 +      struct irte *irte = &data->irte_entry;
 +      struct msi_msg *msg = &data->msi_entry;
 +
 +      prepare_irte(irte, irq_cfg->vector, irq_cfg->dest_apicid);
 +      switch (info->type) {
 +      case X86_IRQ_ALLOC_TYPE_IOAPIC:
 +              /* Set source-id of interrupt request */
 +              set_ioapic_sid(irte, info->ioapic_id);
 +              apic_printk(APIC_VERBOSE, KERN_DEBUG "IOAPIC[%d]: Set IRTE entry (P:%d FPD:%d Dst_Mode:%d Redir_hint:%d Trig_Mode:%d Dlvry_Mode:%X Avail:%X Vector:%02X Dest:%08X SID:%04X SQ:%X SVT:%X)\n",
 +                      info->ioapic_id, irte->present, irte->fpd,
 +                      irte->dst_mode, irte->redir_hint,
 +                      irte->trigger_mode, irte->dlvry_mode,
 +                      irte->avail, irte->vector, irte->dest_id,
 +                      irte->sid, irte->sq, irte->svt);
 +
 +              entry = (struct IR_IO_APIC_route_entry *)info->ioapic_entry;
 +              info->ioapic_entry = NULL;
 +              memset(entry, 0, sizeof(*entry));
 +              entry->index2   = (index >> 15) & 0x1;
 +              entry->zero     = 0;
 +              entry->format   = 1;
 +              entry->index    = (index & 0x7fff);
 +              /*
 +               * IO-APIC RTE will be configured with virtual vector.
 +               * irq handler will do the explicit EOI to the io-apic.
 +               */
 +              entry->vector   = info->ioapic_pin;
 +              entry->mask     = 0;                    /* enable IRQ */
 +              entry->trigger  = info->ioapic_trigger;
 +              entry->polarity = info->ioapic_polarity;
 +              if (info->ioapic_trigger)
 +                      entry->mask = 1; /* Mask level triggered irqs. */
 +              break;
 +
 +      case X86_IRQ_ALLOC_TYPE_HPET:
 +      case X86_IRQ_ALLOC_TYPE_MSI:
 +      case X86_IRQ_ALLOC_TYPE_MSIX:
 +              if (info->type == X86_IRQ_ALLOC_TYPE_HPET)
 +                      set_hpet_sid(irte, info->hpet_id);
 +              else
 +                      set_msi_sid(irte, info->msi_dev);
 +
 +              msg->address_hi = MSI_ADDR_BASE_HI;
 +              msg->data = sub_handle;
 +              msg->address_lo = MSI_ADDR_BASE_LO | MSI_ADDR_IR_EXT_INT |
 +                                MSI_ADDR_IR_SHV |
 +                                MSI_ADDR_IR_INDEX1(index) |
 +                                MSI_ADDR_IR_INDEX2(index);
 +              break;
 +
 +      default:
 +              BUG_ON(1);
 +              break;
 +      }
 +}
  
 -      down_read(&dmar_global_lock);
 -      iommu = map_dev_to_ir(dev);
 -      if (!iommu) {
 -              pr_err("Unable to map PCI %s to iommu\n", pci_name(dev));
 -              index = -ENOENT;
 -      } else {
 -              index = alloc_irte(iommu, irq, nvec);
 -              if (index < 0) {
 -                      pr_err("Unable to allocate %d IRTE for PCI %s\n",
 -                             nvec, pci_name(dev));
 -                      index = -ENOSPC;
 +static void intel_free_irq_resources(struct irq_domain *domain,
 +                                   unsigned int virq, unsigned int nr_irqs)
 +{
 +      struct irq_data *irq_data;
 +      struct intel_ir_data *data;
 +      struct irq_2_iommu *irq_iommu;
 +      unsigned long flags;
 +      int i;
 +      for (i = 0; i < nr_irqs; i++) {
 +              irq_data = irq_domain_get_irq_data(domain, virq  + i);
 +              if (irq_data && irq_data->chip_data) {
 +                      data = irq_data->chip_data;
 +                      irq_iommu = &data->irq_2_iommu;
 +                      raw_spin_lock_irqsave(&irq_2_ir_lock, flags);
 +                      clear_entries(irq_iommu);
 +                      raw_spin_unlock_irqrestore(&irq_2_ir_lock, flags);
 +                      irq_domain_reset_irq_data(irq_data);
 +                      kfree(data);
                }
        }
 -      up_read(&dmar_global_lock);
 -
 -      return index;
  }
  
 -static int intel_msi_setup_irq(struct pci_dev *pdev, unsigned int irq,
 -                             int index, int sub_handle)
 +static int intel_irq_remapping_alloc(struct irq_domain *domain,
 +                                   unsigned int virq, unsigned int nr_irqs,
 +                                   void *arg)
  {
 -      struct intel_iommu *iommu;
 -      int ret = -ENOENT;
 +      struct intel_iommu *iommu = domain->host_data;
 +      struct irq_alloc_info *info = arg;
 +      struct intel_ir_data *data, *ird;
 +      struct irq_data *irq_data;
 +      struct irq_cfg *irq_cfg;
 +      int i, ret, index;
 +
 +      if (!info || !iommu)
 +              return -EINVAL;
 +      if (nr_irqs > 1 && info->type != X86_IRQ_ALLOC_TYPE_MSI &&
 +          info->type != X86_IRQ_ALLOC_TYPE_MSIX)
 +              return -EINVAL;
 +
 +      /*
 +       * With IRQ remapping enabled, don't need contiguous CPU vectors
 +       * to support multiple MSI interrupts.
 +       */
 +      if (info->type == X86_IRQ_ALLOC_TYPE_MSI)
 +              info->flags &= ~X86_IRQ_ALLOC_CONTIGUOUS_VECTORS;
 +
 +      ret = irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, arg);
 +      if (ret < 0)
 +              return ret;
 +
 +      ret = -ENOMEM;
 +      data = kzalloc(sizeof(*data), GFP_KERNEL);
 +      if (!data)
 +              goto out_free_parent;
  
        down_read(&dmar_global_lock);
 -      iommu = map_dev_to_ir(pdev);
 -      if (iommu) {
 -              /*
 -               * setup the mapping between the irq and the IRTE
 -               * base index, the sub_handle pointing to the
 -               * appropriate interrupt remap table entry.
 -               */
 -              set_irte_irq(irq, iommu, index, sub_handle);
 -              ret = 0;
 -      }
 +      index = alloc_irte(iommu, virq, &data->irq_2_iommu, nr_irqs);
        up_read(&dmar_global_lock);
 +      if (index < 0) {
 +              pr_warn("Failed to allocate IRTE\n");
 +              kfree(data);
 +              goto out_free_parent;
 +      }
  
 +      for (i = 0; i < nr_irqs; i++) {
 +              irq_data = irq_domain_get_irq_data(domain, virq + i);
 +              irq_cfg = irqd_cfg(irq_data);
 +              if (!irq_data || !irq_cfg) {
 +                      ret = -EINVAL;
 +                      goto out_free_data;
 +              }
 +
 +              if (i > 0) {
 +                      ird = kzalloc(sizeof(*ird), GFP_KERNEL);
 +                      if (!ird)
 +                              goto out_free_data;
 +                      /* Initialize the common data */
 +                      ird->irq_2_iommu = data->irq_2_iommu;
 +                      ird->irq_2_iommu.sub_handle = i;
 +              } else {
 +                      ird = data;
 +              }
 +
 +              irq_data->hwirq = (index << 16) + i;
 +              irq_data->chip_data = ird;
 +              irq_data->chip = &intel_ir_chip;
 +              intel_irq_remapping_prepare_irte(ird, irq_cfg, info, index, i);
 +              irq_set_status_flags(virq + i, IRQ_MOVE_PCNTXT);
 +      }
 +      return 0;
 +
 +out_free_data:
 +      intel_free_irq_resources(domain, virq, i);
 +out_free_parent:
 +      irq_domain_free_irqs_common(domain, virq, nr_irqs);
        return ret;
  }
  
Simple merge