]> Git Repo - linux.git/commitdiff
Merge tag 'irq-core-2020-12-15' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorLinus Torvalds <[email protected]>
Tue, 15 Dec 2020 23:03:31 +0000 (15:03 -0800)
committerLinus Torvalds <[email protected]>
Tue, 15 Dec 2020 23:03:31 +0000 (15:03 -0800)
Pull irq updates from Thomas Gleixner:
 "Generic interrupt and irqchips subsystem updates. Unusually, there is
  not a single completely new irq chip driver, just new DT bindings and
  extensions of existing drivers to accomodate new variants!

  Core:

   - Consolidation and robustness changes for irq time accounting

   - Cleanup and consolidation of irq stats

   - Remove the fasteoi IPI flow which has been proved useless

   - Provide an interface for converting legacy interrupt mechanism into
     irqdomains

  Drivers:

   - Preliminary support for managed interrupts on platform devices

   - Correctly identify allocation of MSIs proxyied by another device

   - Generalise the Ocelot support to new SoCs

   - Improve GICv4.1 vcpu entry, matching the corresponding KVM
     optimisation

   - Work around spurious interrupts on Qualcomm PDC

   - Random fixes and cleanups"

* tag 'irq-core-2020-12-15' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (54 commits)
  irqchip/qcom-pdc: Fix phantom irq when changing between rising/falling
  driver core: platform: Add devm_platform_get_irqs_affinity()
  ACPI: Drop acpi_dev_irqresource_disabled()
  resource: Add irqresource_disabled()
  genirq/affinity: Add irq_update_affinity_desc()
  irqchip/gic-v3-its: Flag device allocation as proxied if behind a PCI bridge
  irqchip/gic-v3-its: Tag ITS device as shared if allocating for a proxy device
  platform-msi: Track shared domain allocation
  irqchip/ti-sci-intr: Fix freeing of irqs
  irqchip/ti-sci-inta: Fix printing of inta id on probe success
  drivers/irqchip: Remove EZChip NPS interrupt controller
  Revert "genirq: Add fasteoi IPI flow"
  irqchip/hip04: Make IPIs use handle_percpu_devid_irq()
  irqchip/bcm2836: Make IPIs use handle_percpu_devid_irq()
  irqchip/armada-370-xp: Make IPIs use handle_percpu_devid_irq()
  irqchip/gic, gic-v3: Make SGIs use handle_percpu_devid_irq()
  irqchip/ocelot: Add support for Jaguar2 platforms
  irqchip/ocelot: Add support for Serval platforms
  irqchip/ocelot: Add support for Luton platforms
  irqchip/ocelot: prepare to support more SoC
  ...

1  2 
arch/Kconfig
arch/s390/Kconfig
drivers/base/platform.c
include/asm-generic/msi.h
include/linux/irqdomain.h
include/linux/platform_device.h
include/linux/preempt.h
kernel/irq/irqdomain.c

diff --combined arch/Kconfig
index becdd2f22d2e9038184739a9f888df2bbbaf71f8,0f151b49c7b7c55c2d6046a08a570a8c33a3bef0..96992b01d80696b4b4097468921bf0a608416b5b
@@@ -261,7 -261,7 +261,7 @@@ config ARCH_HAS_SET_DIRECT_MA
  
  #
  # Select if the architecture provides the arch_dma_set_uncached symbol to
 -# either provide an uncached segement alias for a DMA allocation, or
 +# either provide an uncached segment alias for a DMA allocation, or
  # to remap the page tables in place.
  #
  config ARCH_HAS_DMA_SET_UNCACHED
@@@ -314,14 -314,14 +314,14 @@@ config ARCH_32BIT_OFF_
  config HAVE_ASM_MODVERSIONS
        bool
        help
 -        This symbol should be selected by an architecure if it provides
 +        This symbol should be selected by an architecture if it provides
          <asm/asm-prototypes.h> to support the module versioning for symbols
          exported from assembly code.
  
  config HAVE_REGS_AND_STACK_ACCESS_API
        bool
        help
 -        This symbol should be selected by an architecure if it supports
 +        This symbol should be selected by an architecture if it supports
          the API needed to access registers and stack entries from pt_regs,
          declared in asm/ptrace.h
          For example the kprobes-based event tracer needs this API.
@@@ -336,7 -336,7 +336,7 @@@ config HAVE_RSE
  config HAVE_FUNCTION_ARG_ACCESS_API
        bool
        help
 -        This symbol should be selected by an architecure if it supports
 +        This symbol should be selected by an architecture if it supports
          the API needed to access function arguments from pt_regs,
          declared in asm/ptrace.h
  
@@@ -618,23 -618,6 +618,23 @@@ config HAVE_CONTEXT_TRACKIN
          protected inside rcu_irq_enter/rcu_irq_exit() but preemption or signal
          handling on irq exit still need to be protected.
  
 +config HAVE_CONTEXT_TRACKING_OFFSTACK
 +      bool
 +      help
 +        Architecture neither relies on exception_enter()/exception_exit()
 +        nor on schedule_user(). Also preempt_schedule_notrace() and
 +        preempt_schedule_irq() can't be called in a preemptible section
 +        while context tracking is CONTEXT_USER. This feature reflects a sane
 +        entry implementation where the following requirements are met on
 +        critical entry code, ie: before user_exit() or after user_enter():
 +
 +        - Critical entry code isn't preemptible (or better yet:
 +          not interruptible).
 +        - No use of RCU read side critical sections, unless rcu_nmi_enter()
 +          got called.
 +        - No use of instrumentation, unless instrumentation_begin() got
 +          called.
 +
  config HAVE_TIF_NOHZ
        bool
        help
  config HAVE_VIRT_CPU_ACCOUNTING
        bool
  
+ config HAVE_VIRT_CPU_ACCOUNTING_IDLE
+       bool
+       help
+         Architecture has its own way to account idle CPU time and therefore
+         doesn't implement vtime_account_idle().
  config ARCH_HAS_SCALED_CPUTIME
        bool
  
@@@ -658,20 -647,12 +664,19 @@@ config HAVE_VIRT_CPU_ACCOUNTING_GE
          some 32-bit arches may require multiple accesses, so proper
          locking is needed to protect against concurrent accesses.
  
  config HAVE_IRQ_TIME_ACCOUNTING
        bool
        help
          Archs need to ensure they use a high enough resolution clock to
          support irq time accounting and then call enable_sched_clock_irqtime().
  
 +config HAVE_MOVE_PUD
 +      bool
 +      help
 +        Architectures that select this are able to move page tables at the
 +        PUD level. If there are only 3 page table levels, the move effectively
 +        happens at the PGD level.
 +
  config HAVE_MOVE_PMD
        bool
        help
@@@ -1052,21 -1033,6 +1057,21 @@@ config HAVE_STATIC_CALL_INLIN
        bool
        depends on HAVE_STATIC_CALL
  
 +config ARCH_WANT_LD_ORPHAN_WARN
 +      bool
 +      help
 +        An arch should select this symbol once all linker sections are explicitly
 +        included, size-asserted, or discarded in the linker scripts. This is
 +        important because we never want expected sections to be placed heuristically
 +        by the linker, since the locations of such sections can change between linker
 +        versions.
 +
 +config HAVE_ARCH_PFN_VALID
 +      bool
 +
 +config ARCH_SUPPORTS_DEBUG_PAGEALLOC
 +      bool
 +
  source "kernel/gcov/Kconfig"
  
  source "scripts/gcc-plugins/Kconfig"
diff --combined arch/s390/Kconfig
index a9b1328e375d4a4b242bfa6d21ee5281aebdfdda,6f1fdcd3b5db706cc453329441e8132de1e66657..cbd81f6156912359619b10c9627538b148f16f00
@@@ -35,6 -35,9 +35,6 @@@ config GENERIC_LOCKBREA
  config PGSTE
        def_bool y if KVM
  
 -config ARCH_SUPPORTS_DEBUG_PAGEALLOC
 -      def_bool y
 -
  config AUDIT_ARCH
        def_bool y
  
@@@ -50,7 -53,8 +50,7 @@@ config ARCH_SUPPORTS_UPROBE
  config KASAN_SHADOW_OFFSET
        hex
        depends on KASAN
 -      default 0x18000000000000 if KASAN_S390_4_LEVEL_PAGING
 -      default 0x30000000000
 +      default 0x18000000000000
  
  config S390
        def_bool y
        select ARCH_INLINE_WRITE_UNLOCK_IRQRESTORE
        select ARCH_STACKWALK
        select ARCH_SUPPORTS_ATOMIC_RMW
 +      select ARCH_SUPPORTS_DEBUG_PAGEALLOC
        select ARCH_SUPPORTS_NUMA_BALANCING
        select ARCH_USE_BUILTIN_BSWAP
        select ARCH_USE_CMPXCHG_LOCKREF
        select HAVE_RSEQ
        select HAVE_SYSCALL_TRACEPOINTS
        select HAVE_VIRT_CPU_ACCOUNTING
+       select HAVE_VIRT_CPU_ACCOUNTING_IDLE
        select IOMMU_HELPER             if PCI
        select IOMMU_SUPPORT            if PCI
        select MODULES_USE_ELF_RELA
        select PCI_DOMAINS              if PCI
        select PCI_MSI                  if PCI
        select PCI_MSI_ARCH_FALLBACKS   if PCI_MSI
 -      select SET_FS
        select SPARSE_IRQ
        select SYSCTL_EXCEPTION_TRACE
        select THREAD_INFO_IN_TASK
@@@ -710,7 -715,7 +711,7 @@@ if PC
  config PCI_NR_FUNCTIONS
        int "Maximum number of PCI functions (1-4096)"
        range 1 4096
 -      default "128"
 +      default "512"
        help
          This allows you to specify the maximum number of PCI functions which
          this kernel will support.
diff --combined drivers/base/platform.c
index e9477e0bbca59dc1a39d4499dfc6d144541dd291,ea8add164b8965a8dd2eb0088ff9b049f01466ba..95fd1549f87de38dcbad0846e923b06ee25bdbd2
@@@ -15,6 -15,8 +15,8 @@@
  #include <linux/of_irq.h>
  #include <linux/module.h>
  #include <linux/init.h>
+ #include <linux/interrupt.h>
+ #include <linux/ioport.h>
  #include <linux/dma-mapping.h>
  #include <linux/memblock.h>
  #include <linux/err.h>
@@@ -63,21 -65,6 +65,21 @@@ struct resource *platform_get_resource(
  }
  EXPORT_SYMBOL_GPL(platform_get_resource);
  
 +struct resource *platform_get_mem_or_io(struct platform_device *dev,
 +                                      unsigned int num)
 +{
 +      u32 i;
 +
 +      for (i = 0; i < dev->num_resources; i++) {
 +              struct resource *r = &dev->resource[i];
 +
 +              if ((resource_type(r) & (IORESOURCE_MEM|IORESOURCE_IO)) && num-- == 0)
 +                      return r;
 +      }
 +      return NULL;
 +}
 +EXPORT_SYMBOL_GPL(platform_get_mem_or_io);
 +
  #ifdef CONFIG_HAS_IOMEM
  /**
   * devm_platform_get_and_ioremap_resource - call devm_ioremap_resource() for a
@@@ -304,6 -291,125 +306,125 @@@ int platform_irq_count(struct platform_
  }
  EXPORT_SYMBOL_GPL(platform_irq_count);
  
+ struct irq_affinity_devres {
+       unsigned int count;
+       unsigned int irq[];
+ };
+ static void platform_disable_acpi_irq(struct platform_device *pdev, int index)
+ {
+       struct resource *r;
+       r = platform_get_resource(pdev, IORESOURCE_IRQ, index);
+       if (r)
+               irqresource_disabled(r, 0);
+ }
+ static void devm_platform_get_irqs_affinity_release(struct device *dev,
+                                                   void *res)
+ {
+       struct irq_affinity_devres *ptr = res;
+       int i;
+       for (i = 0; i < ptr->count; i++) {
+               irq_dispose_mapping(ptr->irq[i]);
+               if (has_acpi_companion(dev))
+                       platform_disable_acpi_irq(to_platform_device(dev), i);
+       }
+ }
+ /**
+  * devm_platform_get_irqs_affinity - devm method to get a set of IRQs for a
+  *                            device using an interrupt affinity descriptor
+  * @dev: platform device pointer
+  * @affd: affinity descriptor
+  * @minvec: minimum count of interrupt vectors
+  * @maxvec: maximum count of interrupt vectors
+  * @irqs: pointer holder for IRQ numbers
+  *
+  * Gets a set of IRQs for a platform device, and updates IRQ afffinty according
+  * to the passed affinity descriptor
+  *
+  * Return: Number of vectors on success, negative error number on failure.
+  */
+ int devm_platform_get_irqs_affinity(struct platform_device *dev,
+                                   struct irq_affinity *affd,
+                                   unsigned int minvec,
+                                   unsigned int maxvec,
+                                   int **irqs)
+ {
+       struct irq_affinity_devres *ptr;
+       struct irq_affinity_desc *desc;
+       size_t size;
+       int i, ret, nvec;
+       if (!affd)
+               return -EPERM;
+       if (maxvec < minvec)
+               return -ERANGE;
+       nvec = platform_irq_count(dev);
+       if (nvec < minvec)
+               return -ENOSPC;
+       nvec = irq_calc_affinity_vectors(minvec, nvec, affd);
+       if (nvec < minvec)
+               return -ENOSPC;
+       if (nvec > maxvec)
+               nvec = maxvec;
+       size = sizeof(*ptr) + sizeof(unsigned int) * nvec;
+       ptr = devres_alloc(devm_platform_get_irqs_affinity_release, size,
+                          GFP_KERNEL);
+       if (!ptr)
+               return -ENOMEM;
+       ptr->count = nvec;
+       for (i = 0; i < nvec; i++) {
+               int irq = platform_get_irq(dev, i);
+               if (irq < 0) {
+                       ret = irq;
+                       goto err_free_devres;
+               }
+               ptr->irq[i] = irq;
+       }
+       desc = irq_create_affinity_masks(nvec, affd);
+       if (!desc) {
+               ret = -ENOMEM;
+               goto err_free_devres;
+       }
+       for (i = 0; i < nvec; i++) {
+               ret = irq_update_affinity_desc(ptr->irq[i], &desc[i]);
+               if (ret) {
+                       dev_err(&dev->dev, "failed to update irq%d affinity descriptor (%d)\n",
+                               ptr->irq[i], ret);
+                       goto err_free_desc;
+               }
+       }
+       devres_add(&dev->dev, ptr);
+       kfree(desc);
+       *irqs = ptr->irq;
+       return nvec;
+ err_free_desc:
+       kfree(desc);
+ err_free_devres:
+       devres_free(ptr);
+       return ret;
+ }
+ EXPORT_SYMBOL_GPL(devm_platform_get_irqs_affinity);
  /**
   * platform_get_resource_byname - get a resource for a device by name
   * @dev: platform device
@@@ -758,6 -864,62 +879,6 @@@ err
  }
  EXPORT_SYMBOL_GPL(platform_device_register_full);
  
 -static int platform_drv_probe(struct device *_dev)
 -{
 -      struct platform_driver *drv = to_platform_driver(_dev->driver);
 -      struct platform_device *dev = to_platform_device(_dev);
 -      int ret;
 -
 -      ret = of_clk_set_defaults(_dev->of_node, false);
 -      if (ret < 0)
 -              return ret;
 -
 -      ret = dev_pm_domain_attach(_dev, true);
 -      if (ret)
 -              goto out;
 -
 -      if (drv->probe) {
 -              ret = drv->probe(dev);
 -              if (ret)
 -                      dev_pm_domain_detach(_dev, true);
 -      }
 -
 -out:
 -      if (drv->prevent_deferred_probe && ret == -EPROBE_DEFER) {
 -              dev_warn(_dev, "probe deferral not supported\n");
 -              ret = -ENXIO;
 -      }
 -
 -      return ret;
 -}
 -
 -static int platform_drv_probe_fail(struct device *_dev)
 -{
 -      return -ENXIO;
 -}
 -
 -static int platform_drv_remove(struct device *_dev)
 -{
 -      struct platform_driver *drv = to_platform_driver(_dev->driver);
 -      struct platform_device *dev = to_platform_device(_dev);
 -      int ret = 0;
 -
 -      if (drv->remove)
 -              ret = drv->remove(dev);
 -      dev_pm_domain_detach(_dev, true);
 -
 -      return ret;
 -}
 -
 -static void platform_drv_shutdown(struct device *_dev)
 -{
 -      struct platform_driver *drv = to_platform_driver(_dev->driver);
 -      struct platform_device *dev = to_platform_device(_dev);
 -
 -      if (drv->shutdown)
 -              drv->shutdown(dev);
 -}
 -
  /**
   * __platform_driver_register - register a driver for platform-level devices
   * @drv: platform driver structure
@@@ -768,6 -930,9 +889,6 @@@ int __platform_driver_register(struct p
  {
        drv->driver.owner = owner;
        drv->driver.bus = &platform_bus_type;
 -      drv->driver.probe = platform_drv_probe;
 -      drv->driver.remove = platform_drv_remove;
 -      drv->driver.shutdown = platform_drv_shutdown;
  
        return driver_register(&drv->driver);
  }
@@@ -783,11 -948,6 +904,11 @@@ void platform_driver_unregister(struct 
  }
  EXPORT_SYMBOL_GPL(platform_driver_unregister);
  
 +static int platform_probe_fail(struct platform_device *pdev)
 +{
 +      return -ENXIO;
 +}
 +
  /**
   * __platform_driver_probe - register driver for non-hotpluggable device
   * @drv: platform driver structure
@@@ -848,9 -1008,10 +969,9 @@@ int __init_or_module __platform_driver_
         * new devices fail.
         */
        spin_lock(&drv->driver.bus->p->klist_drivers.k_lock);
 -      drv->probe = NULL;
 +      drv->probe = platform_probe_fail;
        if (code == 0 && list_empty(&drv->driver.p->klist_devices.k_list))
                retval = -ENODEV;
 -      drv->driver.probe = platform_drv_probe_fail;
        spin_unlock(&drv->driver.bus->p->klist_drivers.k_lock);
  
        if (code != retval)
@@@ -977,6 -1138,129 +1098,6 @@@ void platform_unregister_drivers(struc
  }
  EXPORT_SYMBOL_GPL(platform_unregister_drivers);
  
 -/* modalias support enables more hands-off userspace setup:
 - * (a) environment variable lets new-style hotplug events work once system is
 - *     fully running:  "modprobe $MODALIAS"
 - * (b) sysfs attribute lets new-style coldplug recover from hotplug events
 - *     mishandled before system is fully running:  "modprobe $(cat modalias)"
 - */
 -static ssize_t modalias_show(struct device *dev,
 -                           struct device_attribute *attr, char *buf)
 -{
 -      struct platform_device *pdev = to_platform_device(dev);
 -      int len;
 -
 -      len = of_device_modalias(dev, buf, PAGE_SIZE);
 -      if (len != -ENODEV)
 -              return len;
 -
 -      len = acpi_device_modalias(dev, buf, PAGE_SIZE - 1);
 -      if (len != -ENODEV)
 -              return len;
 -
 -      return sysfs_emit(buf, "platform:%s\n", pdev->name);
 -}
 -static DEVICE_ATTR_RO(modalias);
 -
 -static ssize_t driver_override_store(struct device *dev,
 -                                   struct device_attribute *attr,
 -                                   const char *buf, size_t count)
 -{
 -      struct platform_device *pdev = to_platform_device(dev);
 -      char *driver_override, *old, *cp;
 -
 -      /* We need to keep extra room for a newline */
 -      if (count >= (PAGE_SIZE - 1))
 -              return -EINVAL;
 -
 -      driver_override = kstrndup(buf, count, GFP_KERNEL);
 -      if (!driver_override)
 -              return -ENOMEM;
 -
 -      cp = strchr(driver_override, '\n');
 -      if (cp)
 -              *cp = '\0';
 -
 -      device_lock(dev);
 -      old = pdev->driver_override;
 -      if (strlen(driver_override)) {
 -              pdev->driver_override = driver_override;
 -      } else {
 -              kfree(driver_override);
 -              pdev->driver_override = NULL;
 -      }
 -      device_unlock(dev);
 -
 -      kfree(old);
 -
 -      return count;
 -}
 -
 -static ssize_t driver_override_show(struct device *dev,
 -                                  struct device_attribute *attr, char *buf)
 -{
 -      struct platform_device *pdev = to_platform_device(dev);
 -      ssize_t len;
 -
 -      device_lock(dev);
 -      len = sysfs_emit(buf, "%s\n", pdev->driver_override);
 -      device_unlock(dev);
 -
 -      return len;
 -}
 -static DEVICE_ATTR_RW(driver_override);
 -
 -static ssize_t numa_node_show(struct device *dev,
 -                            struct device_attribute *attr, char *buf)
 -{
 -      return sysfs_emit(buf, "%d\n", dev_to_node(dev));
 -}
 -static DEVICE_ATTR_RO(numa_node);
 -
 -static umode_t platform_dev_attrs_visible(struct kobject *kobj, struct attribute *a,
 -              int n)
 -{
 -      struct device *dev = container_of(kobj, typeof(*dev), kobj);
 -
 -      if (a == &dev_attr_numa_node.attr &&
 -                      dev_to_node(dev) == NUMA_NO_NODE)
 -              return 0;
 -
 -      return a->mode;
 -}
 -
 -static struct attribute *platform_dev_attrs[] = {
 -      &dev_attr_modalias.attr,
 -      &dev_attr_numa_node.attr,
 -      &dev_attr_driver_override.attr,
 -      NULL,
 -};
 -
 -static struct attribute_group platform_dev_group = {
 -      .attrs = platform_dev_attrs,
 -      .is_visible = platform_dev_attrs_visible,
 -};
 -__ATTRIBUTE_GROUPS(platform_dev);
 -
 -static int platform_uevent(struct device *dev, struct kobj_uevent_env *env)
 -{
 -      struct platform_device  *pdev = to_platform_device(dev);
 -      int rc;
 -
 -      /* Some devices have extra OF data and an OF-style MODALIAS */
 -      rc = of_device_uevent_modalias(dev, env);
 -      if (rc != -ENODEV)
 -              return rc;
 -
 -      rc = acpi_device_uevent_modalias(dev, env);
 -      if (rc != -ENODEV)
 -              return rc;
 -
 -      add_uevent_var(env, "MODALIAS=%s%s", PLATFORM_MODULE_PREFIX,
 -                      pdev->name);
 -      return 0;
 -}
 -
  static const struct platform_device_id *platform_match_id(
                        const struct platform_device_id *id,
                        struct platform_device *pdev)
        return NULL;
  }
  
 -/**
 - * platform_match - bind platform device to platform driver.
 - * @dev: device.
 - * @drv: driver.
 - *
 - * Platform device IDs are assumed to be encoded like this:
 - * "<name><instance>", where <name> is a short description of the type of
 - * device, like "pci" or "floppy", and <instance> is the enumerated
 - * instance of the device, like '0' or '42'.  Driver IDs are simply
 - * "<name>".  So, extract the <name> from the platform_device structure,
 - * and compare it against the name of the driver. Return whether they match
 - * or not.
 - */
 -static int platform_match(struct device *dev, struct device_driver *drv)
 -{
 -      struct platform_device *pdev = to_platform_device(dev);
 -      struct platform_driver *pdrv = to_platform_driver(drv);
 -
 -      /* When driver_override is set, only bind to the matching driver */
 -      if (pdev->driver_override)
 -              return !strcmp(pdev->driver_override, drv->name);
 -
 -      /* Attempt an OF style match first */
 -      if (of_driver_match_device(dev, drv))
 -              return 1;
 -
 -      /* Then try ACPI style match */
 -      if (acpi_driver_match_device(dev, drv))
 -              return 1;
 -
 -      /* Then try to match against the id table */
 -      if (pdrv->id_table)
 -              return platform_match_id(pdrv->id_table, pdev) != NULL;
 -
 -      /* fall-back to driver name match */
 -      return (strcmp(pdev->name, drv->name) == 0);
 -}
 -
  #ifdef CONFIG_PM_SLEEP
  
  static int platform_legacy_suspend(struct device *dev, pm_message_t mesg)
@@@ -1135,234 -1457,6 +1256,234 @@@ int platform_pm_restore(struct device *
  
  #endif /* CONFIG_HIBERNATE_CALLBACKS */
  
 +/* modalias support enables more hands-off userspace setup:
 + * (a) environment variable lets new-style hotplug events work once system is
 + *     fully running:  "modprobe $MODALIAS"
 + * (b) sysfs attribute lets new-style coldplug recover from hotplug events
 + *     mishandled before system is fully running:  "modprobe $(cat modalias)"
 + */
 +static ssize_t modalias_show(struct device *dev,
 +                           struct device_attribute *attr, char *buf)
 +{
 +      struct platform_device *pdev = to_platform_device(dev);
 +      int len;
 +
 +      len = of_device_modalias(dev, buf, PAGE_SIZE);
 +      if (len != -ENODEV)
 +              return len;
 +
 +      len = acpi_device_modalias(dev, buf, PAGE_SIZE - 1);
 +      if (len != -ENODEV)
 +              return len;
 +
 +      return sysfs_emit(buf, "platform:%s\n", pdev->name);
 +}
 +static DEVICE_ATTR_RO(modalias);
 +
 +static ssize_t numa_node_show(struct device *dev,
 +                            struct device_attribute *attr, char *buf)
 +{
 +      return sysfs_emit(buf, "%d\n", dev_to_node(dev));
 +}
 +static DEVICE_ATTR_RO(numa_node);
 +
 +static ssize_t driver_override_show(struct device *dev,
 +                                  struct device_attribute *attr, char *buf)
 +{
 +      struct platform_device *pdev = to_platform_device(dev);
 +      ssize_t len;
 +
 +      device_lock(dev);
 +      len = sysfs_emit(buf, "%s\n", pdev->driver_override);
 +      device_unlock(dev);
 +
 +      return len;
 +}
 +
 +static ssize_t driver_override_store(struct device *dev,
 +                                   struct device_attribute *attr,
 +                                   const char *buf, size_t count)
 +{
 +      struct platform_device *pdev = to_platform_device(dev);
 +      char *driver_override, *old, *cp;
 +
 +      /* We need to keep extra room for a newline */
 +      if (count >= (PAGE_SIZE - 1))
 +              return -EINVAL;
 +
 +      driver_override = kstrndup(buf, count, GFP_KERNEL);
 +      if (!driver_override)
 +              return -ENOMEM;
 +
 +      cp = strchr(driver_override, '\n');
 +      if (cp)
 +              *cp = '\0';
 +
 +      device_lock(dev);
 +      old = pdev->driver_override;
 +      if (strlen(driver_override)) {
 +              pdev->driver_override = driver_override;
 +      } else {
 +              kfree(driver_override);
 +              pdev->driver_override = NULL;
 +      }
 +      device_unlock(dev);
 +
 +      kfree(old);
 +
 +      return count;
 +}
 +static DEVICE_ATTR_RW(driver_override);
 +
 +static struct attribute *platform_dev_attrs[] = {
 +      &dev_attr_modalias.attr,
 +      &dev_attr_numa_node.attr,
 +      &dev_attr_driver_override.attr,
 +      NULL,
 +};
 +
 +static umode_t platform_dev_attrs_visible(struct kobject *kobj, struct attribute *a,
 +              int n)
 +{
 +      struct device *dev = container_of(kobj, typeof(*dev), kobj);
 +
 +      if (a == &dev_attr_numa_node.attr &&
 +                      dev_to_node(dev) == NUMA_NO_NODE)
 +              return 0;
 +
 +      return a->mode;
 +}
 +
 +static struct attribute_group platform_dev_group = {
 +      .attrs = platform_dev_attrs,
 +      .is_visible = platform_dev_attrs_visible,
 +};
 +__ATTRIBUTE_GROUPS(platform_dev);
 +
 +
 +/**
 + * platform_match - bind platform device to platform driver.
 + * @dev: device.
 + * @drv: driver.
 + *
 + * Platform device IDs are assumed to be encoded like this:
 + * "<name><instance>", where <name> is a short description of the type of
 + * device, like "pci" or "floppy", and <instance> is the enumerated
 + * instance of the device, like '0' or '42'.  Driver IDs are simply
 + * "<name>".  So, extract the <name> from the platform_device structure,
 + * and compare it against the name of the driver. Return whether they match
 + * or not.
 + */
 +static int platform_match(struct device *dev, struct device_driver *drv)
 +{
 +      struct platform_device *pdev = to_platform_device(dev);
 +      struct platform_driver *pdrv = to_platform_driver(drv);
 +
 +      /* When driver_override is set, only bind to the matching driver */
 +      if (pdev->driver_override)
 +              return !strcmp(pdev->driver_override, drv->name);
 +
 +      /* Attempt an OF style match first */
 +      if (of_driver_match_device(dev, drv))
 +              return 1;
 +
 +      /* Then try ACPI style match */
 +      if (acpi_driver_match_device(dev, drv))
 +              return 1;
 +
 +      /* Then try to match against the id table */
 +      if (pdrv->id_table)
 +              return platform_match_id(pdrv->id_table, pdev) != NULL;
 +
 +      /* fall-back to driver name match */
 +      return (strcmp(pdev->name, drv->name) == 0);
 +}
 +
 +static int platform_uevent(struct device *dev, struct kobj_uevent_env *env)
 +{
 +      struct platform_device  *pdev = to_platform_device(dev);
 +      int rc;
 +
 +      /* Some devices have extra OF data and an OF-style MODALIAS */
 +      rc = of_device_uevent_modalias(dev, env);
 +      if (rc != -ENODEV)
 +              return rc;
 +
 +      rc = acpi_device_uevent_modalias(dev, env);
 +      if (rc != -ENODEV)
 +              return rc;
 +
 +      add_uevent_var(env, "MODALIAS=%s%s", PLATFORM_MODULE_PREFIX,
 +                      pdev->name);
 +      return 0;
 +}
 +
 +static int platform_probe(struct device *_dev)
 +{
 +      struct platform_driver *drv = to_platform_driver(_dev->driver);
 +      struct platform_device *dev = to_platform_device(_dev);
 +      int ret;
 +
 +      /*
 +       * A driver registered using platform_driver_probe() cannot be bound
 +       * again later because the probe function usually lives in __init code
 +       * and so is gone. For these drivers .probe is set to
 +       * platform_probe_fail in __platform_driver_probe(). Don't even prepare
 +       * clocks and PM domains for these to match the traditional behaviour.
 +       */
 +      if (unlikely(drv->probe == platform_probe_fail))
 +              return -ENXIO;
 +
 +      ret = of_clk_set_defaults(_dev->of_node, false);
 +      if (ret < 0)
 +              return ret;
 +
 +      ret = dev_pm_domain_attach(_dev, true);
 +      if (ret)
 +              goto out;
 +
 +      if (drv->probe) {
 +              ret = drv->probe(dev);
 +              if (ret)
 +                      dev_pm_domain_detach(_dev, true);
 +      }
 +
 +out:
 +      if (drv->prevent_deferred_probe && ret == -EPROBE_DEFER) {
 +              dev_warn(_dev, "probe deferral not supported\n");
 +              ret = -ENXIO;
 +      }
 +
 +      return ret;
 +}
 +
 +static int platform_remove(struct device *_dev)
 +{
 +      struct platform_driver *drv = to_platform_driver(_dev->driver);
 +      struct platform_device *dev = to_platform_device(_dev);
 +      int ret = 0;
 +
 +      if (drv->remove)
 +              ret = drv->remove(dev);
 +      dev_pm_domain_detach(_dev, true);
 +
 +      return ret;
 +}
 +
 +static void platform_shutdown(struct device *_dev)
 +{
 +      struct platform_device *dev = to_platform_device(_dev);
 +      struct platform_driver *drv;
 +
 +      if (!_dev->driver)
 +              return;
 +
 +      drv = to_platform_driver(_dev->driver);
 +      if (drv->shutdown)
 +              drv->shutdown(dev);
 +}
 +
 +
  int platform_dma_configure(struct device *dev)
  {
        enum dev_dma_attr attr;
@@@ -1389,9 -1483,6 +1510,9 @@@ struct bus_type platform_bus_type = 
        .dev_groups     = platform_dev_groups,
        .match          = platform_match,
        .uevent         = platform_uevent,
 +      .probe          = platform_probe,
 +      .remove         = platform_remove,
 +      .shutdown       = platform_shutdown,
        .dma_configure  = platform_dma_configure,
        .pm             = &platform_dev_pm_ops,
  };
index 25344de0e8f9d85e1deee0f75cc55e3beecb7f30,1010e74cb8e054375e03230b2b281cc1d10c226d..bf910d47e900a954c6ea3e9ab5fdc3229c03112c
@@@ -4,8 -4,6 +4,8 @@@
  
  #include <linux/types.h>
  
 +#ifdef CONFIG_GENERIC_MSI_IRQ_DOMAIN
 +
  #ifndef NUM_MSI_ALLOC_SCRATCHPAD_REGS
  # define NUM_MSI_ALLOC_SCRATCHPAD_REGS        2
  #endif
@@@ -24,14 -22,16 +24,18 @@@ struct msi_desc
  typedef struct msi_alloc_info {
        struct msi_desc                 *desc;
        irq_hw_number_t                 hwirq;
+       unsigned long                   flags;
        union {
                unsigned long           ul;
                void                    *ptr;
        } scratchpad[NUM_MSI_ALLOC_SCRATCHPAD_REGS];
  } msi_alloc_info_t;
  
+ /* Device generating MSIs is proxying for another device */
+ #define MSI_ALLOC_FLAGS_PROXY_DEVICE  (1UL << 0)
  #define GENERIC_MSI_DOMAIN_OPS                1
  
 +#endif /* CONFIG_GENERIC_MSI_IRQ_DOMAIN */
 +
  #endif
index ea5a337e0f8b80ac8c97197039c4c391f8675096,5701a8b01726be4cc71ecfa6910fadd35a3ee446..42d196805f587e8adafe294ec93612e9f3c09cf9
@@@ -37,8 -37,8 +37,8 @@@
  #include <linux/radix-tree.h>
  
  struct device_node;
+ struct fwnode_handle;
  struct irq_domain;
- struct of_device_id;
  struct irq_chip;
  struct irq_data;
  struct cpumask;
@@@ -271,6 -271,12 +271,12 @@@ struct irq_domain *irq_domain_add_legac
                                         irq_hw_number_t first_hwirq,
                                         const struct irq_domain_ops *ops,
                                         void *host_data);
+ struct irq_domain *irq_domain_create_legacy(struct fwnode_handle *fwnode,
+                                           unsigned int size,
+                                           unsigned int first_irq,
+                                           irq_hw_number_t first_hwirq,
+                                           const struct irq_domain_ops *ops,
+                                           void *host_data);
  extern struct irq_domain *irq_find_matching_fwspec(struct irq_fwspec *fwspec,
                                                   enum irq_domain_bus_token bus_token);
  extern bool irq_domain_check_msi_remap(void);
@@@ -381,22 -387,12 +387,20 @@@ extern int irq_domain_associate(struct 
  extern void irq_domain_associate_many(struct irq_domain *domain,
                                      unsigned int irq_base,
                                      irq_hw_number_t hwirq_base, int count);
- extern void irq_domain_disassociate(struct irq_domain *domain,
-                                   unsigned int irq);
  
 -extern unsigned int irq_create_mapping(struct irq_domain *host,
 -                                     irq_hw_number_t hwirq);
 +extern unsigned int irq_create_mapping_affinity(struct irq_domain *host,
 +                                    irq_hw_number_t hwirq,
 +                                    const struct irq_affinity_desc *affinity);
  extern unsigned int irq_create_fwspec_mapping(struct irq_fwspec *fwspec);
  extern void irq_dispose_mapping(unsigned int virq);
  
 +static inline unsigned int irq_create_mapping(struct irq_domain *host,
 +                                            irq_hw_number_t hwirq)
 +{
 +      return irq_create_mapping_affinity(host, hwirq, NULL);
 +}
 +
 +
  /**
   * irq_linear_revmap() - Find a linux irq from a hw irq number.
   * @domain: domain owning this hardware interrupt
index ee6a9f10c2c73803761e386d1bfee44a83155ac9,4d75633e67354984f97f3098b42869a70738c5c1..3f23f6e430bfa514d7866a941413ea0d274587f7
@@@ -15,6 -15,7 +15,7 @@@
  #define PLATFORM_DEVID_NONE   (-1)
  #define PLATFORM_DEVID_AUTO   (-2)
  
+ struct irq_affinity;
  struct mfd_cell;
  struct property_entry;
  struct platform_device_id;
@@@ -52,9 -53,6 +53,9 @@@ extern struct device platform_bus
  
  extern struct resource *platform_get_resource(struct platform_device *,
                                              unsigned int, unsigned int);
 +extern struct resource *platform_get_mem_or_io(struct platform_device *,
 +                                             unsigned int);
 +
  extern struct device *
  platform_find_device_by_driver(struct device *start,
                               const struct device_driver *drv);
@@@ -73,6 -71,11 +74,11 @@@ devm_platform_ioremap_resource_byname(s
  extern int platform_get_irq(struct platform_device *, unsigned int);
  extern int platform_get_irq_optional(struct platform_device *, unsigned int);
  extern int platform_irq_count(struct platform_device *);
+ extern int devm_platform_get_irqs_affinity(struct platform_device *dev,
+                                          struct irq_affinity *affd,
+                                          unsigned int minvec,
+                                          unsigned int maxvec,
+                                          int **irqs);
  extern struct resource *platform_get_resource_byname(struct platform_device *,
                                                     unsigned int,
                                                     const char *);
diff --combined include/linux/preempt.h
index 6df63cbe8bb045bc17a1becb771d6d02494e24c2,7547857516d51acec6b2428a42cb5f46ab254f93..69cc8b64aa3a0e3b957fff471b15adcd1dbaa5fe
  /* preempt_count() and related functions, depends on PREEMPT_NEED_RESCHED */
  #include <asm/preempt.h>
  
+ #define nmi_count()   (preempt_count() & NMI_MASK)
  #define hardirq_count()       (preempt_count() & HARDIRQ_MASK)
  #define softirq_count()       (preempt_count() & SOFTIRQ_MASK)
- #define irq_count()   (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK \
-                                | NMI_MASK))
+ #define irq_count()   (nmi_count() | hardirq_count() | softirq_count())
  
  /*
-  * Are we doing bottom half or hardware interrupt processing?
+  * Macros to retrieve the current execution context:
   *
-  * in_irq()       - We're in (hard) IRQ context
+  * in_nmi()           - We're in NMI context
+  * in_hardirq()               - We're in hard IRQ context
+  * in_serving_softirq()       - We're in softirq context
+  * in_task()          - We're in task context
+  */
+ #define in_nmi()              (nmi_count())
+ #define in_hardirq()          (hardirq_count())
+ #define in_serving_softirq()  (softirq_count() & SOFTIRQ_OFFSET)
+ #define in_task()             (!(in_nmi() | in_hardirq() | in_serving_softirq()))
+ /*
+  * The following macros are deprecated and should not be used in new code:
+  * in_irq()       - Obsolete version of in_hardirq()
   * in_softirq()   - We have BH disabled, or are processing softirqs
   * in_interrupt() - We're in NMI,IRQ,SoftIRQ context or have BH disabled
-  * in_serving_softirq() - We're in softirq context
-  * in_nmi()       - We're in NMI context
-  * in_task()    - We're in task context
-  *
-  * Note: due to the BH disabled confusion: in_softirq(),in_interrupt() really
-  *       should not be used in new code.
   */
  #define in_irq()              (hardirq_count())
  #define in_softirq()          (softirq_count())
  #define in_interrupt()                (irq_count())
- #define in_serving_softirq()  (softirq_count() & SOFTIRQ_OFFSET)
- #define in_nmi()              (preempt_count() & NMI_MASK)
- #define in_task()             (!(preempt_count() & \
-                                  (NMI_MASK | HARDIRQ_MASK | SOFTIRQ_OFFSET)))
  
  /*
   * The preempt_count offset after preempt_disable();
@@@ -322,71 -324,34 +324,71 @@@ static inline void preempt_notifier_ini
  
  #endif
  
 -/**
 - * migrate_disable - Prevent migration of the current task
 +#ifdef CONFIG_SMP
 +
 +/*
 + * Migrate-Disable and why it is undesired.
   *
 - * Maps to preempt_disable() which also disables preemption. Use
 - * migrate_disable() to annotate that the intent is to prevent migration,
 - * but not necessarily preemption.
 + * When a preempted task becomes elegible to run under the ideal model (IOW it
 + * becomes one of the M highest priority tasks), it might still have to wait
 + * for the preemptee's migrate_disable() section to complete. Thereby suffering
 + * a reduction in bandwidth in the exact duration of the migrate_disable()
 + * section.
   *
 - * Can be invoked nested like preempt_disable() and needs the corresponding
 - * number of migrate_enable() invocations.
 - */
 -static __always_inline void migrate_disable(void)
 -{
 -      preempt_disable();
 -}
 -
 -/**
 - * migrate_enable - Allow migration of the current task
 + * Per this argument, the change from preempt_disable() to migrate_disable()
 + * gets us:
 + *
 + * - a higher priority tasks gains reduced wake-up latency; with preempt_disable()
 + *   it would have had to wait for the lower priority task.
 + *
 + * - a lower priority tasks; which under preempt_disable() could've instantly
 + *   migrated away when another CPU becomes available, is now constrained
 + *   by the ability to push the higher priority task away, which might itself be
 + *   in a migrate_disable() section, reducing it's available bandwidth.
 + *
 + * IOW it trades latency / moves the interference term, but it stays in the
 + * system, and as long as it remains unbounded, the system is not fully
 + * deterministic.
 + *
 + *
 + * The reason we have it anyway.
   *
 - * Counterpart to migrate_disable().
 + * PREEMPT_RT breaks a number of assumptions traditionally held. By forcing a
 + * number of primitives into becoming preemptible, they would also allow
 + * migration. This turns out to break a bunch of per-cpu usage. To this end,
 + * all these primitives employ migirate_disable() to restore this implicit
 + * assumption.
   *
 - * As migrate_disable() can be invoked nested, only the outermost invocation
 - * reenables migration.
 + * This is a 'temporary' work-around at best. The correct solution is getting
 + * rid of the above assumptions and reworking the code to employ explicit
 + * per-cpu locking or short preempt-disable regions.
 + *
 + * The end goal must be to get rid of migrate_disable(), alternatively we need
 + * a schedulability theory that does not depend on abritrary migration.
 + *
 + *
 + * Notes on the implementation.
 + *
 + * The implementation is particularly tricky since existing code patterns
 + * dictate neither migrate_disable() nor migrate_enable() is allowed to block.
 + * This means that it cannot use cpus_read_lock() to serialize against hotplug,
 + * nor can it easily migrate itself into a pending affinity mask change on
 + * migrate_enable().
 + *
 + *
 + * Note: even non-work-conserving schedulers like semi-partitioned depends on
 + *       migration, so migrate_disable() is not only a problem for
 + *       work-conserving schedulers.
   *
 - * Currently mapped to preempt_enable().
   */
 -static __always_inline void migrate_enable(void)
 -{
 -      preempt_enable();
 -}
 +extern void migrate_disable(void);
 +extern void migrate_enable(void);
 +
 +#else
 +
 +static inline void migrate_disable(void) { }
 +static inline void migrate_enable(void) { }
 +
 +#endif /* CONFIG_SMP */
  
  #endif /* __LINUX_PREEMPT_H */
diff --combined kernel/irq/irqdomain.c
index a21acac9a71afa77d6f619395fbdfeeed19669bd,30a78872a5cf357778d074fe488fec4fe09e8dd4..6aacd342cd14cb88a6e305f4d2473df1bcc5de3b
@@@ -42,16 -42,7 +42,16 @@@ static inline void debugfs_add_domain_d
  static inline void debugfs_remove_domain_dir(struct irq_domain *d) { }
  #endif
  
 -const struct fwnode_operations irqchip_fwnode_ops;
 +static const char *irqchip_fwnode_get_name(const struct fwnode_handle *fwnode)
 +{
 +      struct irqchip_fwid *fwid = container_of(fwnode, struct irqchip_fwid, fwnode);
 +
 +      return fwid->name;
 +}
 +
 +const struct fwnode_operations irqchip_fwnode_ops = {
 +      .get_name = irqchip_fwnode_get_name,
 +};
  EXPORT_SYMBOL_GPL(irqchip_fwnode_ops);
  
  /**
@@@ -100,7 -91,7 +100,7 @@@ struct fwnode_handle *__irq_domain_allo
        fwid->type = type;
        fwid->name = n;
        fwid->pa = pa;
 -      fwid->fwnode.ops = &irqchip_fwnode_ops;
 +      fwnode_init(&fwid->fwnode, &irqchip_fwnode_ops);
        return &fwid->fwnode;
  }
  EXPORT_SYMBOL_GPL(__irq_domain_alloc_fwnode);
@@@ -359,17 -350,28 +359,28 @@@ struct irq_domain *irq_domain_add_legac
                                         irq_hw_number_t first_hwirq,
                                         const struct irq_domain_ops *ops,
                                         void *host_data)
+ {
+       return irq_domain_create_legacy(of_node_to_fwnode(of_node), size,
+                                       first_irq, first_hwirq, ops, host_data);
+ }
+ EXPORT_SYMBOL_GPL(irq_domain_add_legacy);
+ struct irq_domain *irq_domain_create_legacy(struct fwnode_handle *fwnode,
+                                        unsigned int size,
+                                        unsigned int first_irq,
+                                        irq_hw_number_t first_hwirq,
+                                        const struct irq_domain_ops *ops,
+                                        void *host_data)
  {
        struct irq_domain *domain;
  
-       domain = __irq_domain_add(of_node_to_fwnode(of_node), first_hwirq + size,
-                                 first_hwirq + size, 0, ops, host_data);
+       domain = __irq_domain_add(fwnode, first_hwirq + size, first_hwirq + size, 0, ops, host_data);
        if (domain)
                irq_domain_associate_many(domain, first_irq, first_hwirq, size);
  
        return domain;
  }
- EXPORT_SYMBOL_GPL(irq_domain_add_legacy);
+ EXPORT_SYMBOL_GPL(irq_domain_create_legacy);
  
  /**
   * irq_find_matching_fwspec() - Locates a domain for a given fwspec
@@@ -494,7 -496,7 +505,7 @@@ static void irq_domain_set_mapping(stru
        }
  }
  
- void irq_domain_disassociate(struct irq_domain *domain, unsigned int irq)
static void irq_domain_disassociate(struct irq_domain *domain, unsigned int irq)
  {
        struct irq_data *irq_data = irq_get_irq_data(irq);
        irq_hw_number_t hwirq;
@@@ -633,19 -635,17 +644,19 @@@ unsigned int irq_create_direct_mapping(
  EXPORT_SYMBOL_GPL(irq_create_direct_mapping);
  
  /**
 - * irq_create_mapping() - Map a hardware interrupt into linux irq space
 + * irq_create_mapping_affinity() - Map a hardware interrupt into linux irq space
   * @domain: domain owning this hardware interrupt or NULL for default domain
   * @hwirq: hardware irq number in that domain space
 + * @affinity: irq affinity
   *
   * Only one mapping per hardware interrupt is permitted. Returns a linux
   * irq number.
   * If the sense/trigger is to be specified, set_irq_type() should be called
   * on the number returned from that call.
   */
 -unsigned int irq_create_mapping(struct irq_domain *domain,
 -                              irq_hw_number_t hwirq)
 +unsigned int irq_create_mapping_affinity(struct irq_domain *domain,
 +                                     irq_hw_number_t hwirq,
 +                                     const struct irq_affinity_desc *affinity)
  {
        struct device_node *of_node;
        int virq;
        }
  
        /* Allocate a virtual interrupt number */
 -      virq = irq_domain_alloc_descs(-1, 1, hwirq, of_node_to_nid(of_node), NULL);
 +      virq = irq_domain_alloc_descs(-1, 1, hwirq, of_node_to_nid(of_node),
 +                                    affinity);
        if (virq <= 0) {
                pr_debug("-> virq allocation failed\n");
                return 0;
  
        return virq;
  }
 -EXPORT_SYMBOL_GPL(irq_create_mapping);
 +EXPORT_SYMBOL_GPL(irq_create_mapping_affinity);
  
  /**
   * irq_create_strict_mappings() - Map a range of hw irqs to fixed linux irqs
@@@ -749,7 -748,7 +760,7 @@@ static void of_phandle_args_to_fwspec(s
  {
        int i;
  
-       fwspec->fwnode = np ? &np->fwnode : NULL;
+       fwspec->fwnode = of_node_to_fwnode(np);
        fwspec->param_count = count;
  
        for (i = 0; i < count; i++)
@@@ -1382,8 -1381,15 +1393,15 @@@ static void irq_domain_free_irqs_hierar
                                           unsigned int irq_base,
                                           unsigned int nr_irqs)
  {
-       if (domain->ops->free)
-               domain->ops->free(domain, irq_base, nr_irqs);
+       unsigned int i;
+       if (!domain->ops->free)
+               return;
+       for (i = 0; i < nr_irqs; i++) {
+               if (irq_domain_get_irq_data(domain, irq_base + i))
+                       domain->ops->free(domain, irq_base + i, 1);
+       }
  }
  
  int irq_domain_alloc_irqs_hierarchy(struct irq_domain *domain,
This page took 0.191972 seconds and 4 git commands to generate.