Merge tag 'stm-for-greg-20160714' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 15 Jul 2016 05:19:11 +0000 (14:19 +0900)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 15 Jul 2016 05:19:11 +0000 (14:19 +0900)
Alexander writes:

intel_th: Fixes -t://git.kernel.org/pub/scm/linux/kernel/git/ash/stm.git
tags/stm-for-greg-20160714
stable

These are:
 * a fix for a modprobe time deadlock
 * a new PCI ID for Kaby Lake PCH-H

1  2 
drivers/hwtracing/intel_th/core.c
drivers/hwtracing/intel_th/intel_th.h

@@@ -23,7 -23,6 +23,7 @@@
  #include <linux/debugfs.h>
  #include <linux/idr.h>
  #include <linux/pci.h>
 +#include <linux/pm_runtime.h>
  #include <linux/dma-mapping.h>
  
  #include "intel_th.h"
@@@ -68,33 -67,23 +68,33 @@@ static int intel_th_probe(struct devic
  
        hubdrv = to_intel_th_driver(hub->dev.driver);
  
 +      pm_runtime_set_active(dev);
 +      pm_runtime_no_callbacks(dev);
 +      pm_runtime_enable(dev);
 +
        ret = thdrv->probe(to_intel_th_device(dev));
        if (ret)
 -              return ret;
 +              goto out_pm;
  
        if (thdrv->attr_group) {
                ret = sysfs_create_group(&thdev->dev.kobj, thdrv->attr_group);
 -              if (ret) {
 -                      thdrv->remove(thdev);
 -
 -                      return ret;
 -              }
 +              if (ret)
 +                      goto out;
        }
  
        if (thdev->type == INTEL_TH_OUTPUT &&
            !intel_th_output_assigned(thdev))
 +              /* does not talk to hardware */
                ret = hubdrv->assign(hub, thdev);
  
 +out:
 +      if (ret)
 +              thdrv->remove(thdev);
 +
 +out_pm:
 +      if (ret)
 +              pm_runtime_disable(dev);
 +
        return ret;
  }
  
@@@ -114,8 -103,6 +114,8 @@@ static int intel_th_remove(struct devic
        if (thdrv->attr_group)
                sysfs_remove_group(&thdev->dev.kobj, thdrv->attr_group);
  
 +      pm_runtime_get_sync(dev);
 +
        thdrv->remove(thdev);
  
        if (intel_th_output_assigned(thdev)) {
                        to_intel_th_driver(dev->parent->driver);
  
                if (hub->dev.driver)
 +                      /* does not talk to hardware */
                        hubdrv->unassign(hub, thdev);
        }
  
 +      pm_runtime_disable(dev);
 +      pm_runtime_set_active(dev);
 +      pm_runtime_enable(dev);
 +
        return 0;
  }
  
@@@ -203,7 -185,6 +203,7 @@@ static int intel_th_output_activate(str
  {
        struct intel_th_driver *thdrv =
                to_intel_th_driver_or_null(thdev->dev.driver);
 +      int ret = 0;
  
        if (!thdrv)
                return -ENODEV;
        if (!try_module_get(thdrv->driver.owner))
                return -ENODEV;
  
 +      pm_runtime_get_sync(&thdev->dev);
 +
        if (thdrv->activate)
 -              return thdrv->activate(thdev);
 +              ret = thdrv->activate(thdev);
 +      else
 +              intel_th_trace_enable(thdev);
  
 -      intel_th_trace_enable(thdev);
 +      if (ret)
 +              pm_runtime_put(&thdev->dev);
  
 -      return 0;
 +      return ret;
  }
  
  static void intel_th_output_deactivate(struct intel_th_device *thdev)
        else
                intel_th_trace_disable(thdev);
  
 +      pm_runtime_put(&thdev->dev);
        module_put(thdrv->driver.owner);
  }
  
@@@ -490,6 -465,38 +490,38 @@@ static struct intel_th_subdevice 
        },
  };
  
+ #ifdef CONFIG_MODULES
+ static void __intel_th_request_hub_module(struct work_struct *work)
+ {
+       struct intel_th *th = container_of(work, struct intel_th,
+                                          request_module_work);
+       request_module("intel_th_%s", th->hub->name);
+ }
+ static int intel_th_request_hub_module(struct intel_th *th)
+ {
+       INIT_WORK(&th->request_module_work, __intel_th_request_hub_module);
+       schedule_work(&th->request_module_work);
+       return 0;
+ }
+ static void intel_th_request_hub_module_flush(struct intel_th *th)
+ {
+       flush_work(&th->request_module_work);
+ }
+ #else
+ static inline int intel_th_request_hub_module(struct intel_th *th)
+ {
+       return -EINVAL;
+ }
+ static inline void intel_th_request_hub_module_flush(struct intel_th *th)
+ {
+ }
+ #endif /* CONFIG_MODULES */
  static int intel_th_populate(struct intel_th *th, struct resource *devres,
                             unsigned int ndevres, int irq)
  {
                /* need switch driver to be loaded to enumerate the rest */
                if (subdev->type == INTEL_TH_SWITCH && !req) {
                        th->hub = thdev;
-                       err = request_module("intel_th_%s", subdev->name);
+                       err = intel_th_request_hub_module(th);
                        if (!err)
                                req++;
                }
@@@ -653,10 -660,6 +685,10 @@@ intel_th_alloc(struct device *dev, stru
  
        dev_set_drvdata(dev, th);
  
 +      pm_runtime_no_callbacks(dev);
 +      pm_runtime_put(dev);
 +      pm_runtime_allow(dev);
 +
        err = intel_th_populate(th, devres, ndevres, irq);
        if (err)
                goto err_chrdev;
        return th;
  
  err_chrdev:
 +      pm_runtime_forbid(dev);
 +
        __unregister_chrdev(th->major, 0, TH_POSSIBLE_OUTPUTS,
                            "intel_th/output");
  
@@@ -683,15 -684,13 +715,16 @@@ void intel_th_free(struct intel_th *th
  {
        int i;
  
+       intel_th_request_hub_module_flush(th);
        for (i = 0; i < TH_SUBDEVICE_MAX; i++)
                if (th->thdev[i] != th->hub)
                        intel_th_device_remove(th->thdev[i]);
  
        intel_th_device_remove(th->hub);
  
 +      pm_runtime_get_sync(th->dev);
 +      pm_runtime_forbid(th->dev);
 +
        __unregister_chrdev(th->major, 0, TH_POSSIBLE_OUTPUTS,
                            "intel_th/output");
  
@@@ -716,7 -715,6 +749,7 @@@ int intel_th_trace_enable(struct intel_
        if (WARN_ON_ONCE(thdev->type != INTEL_TH_OUTPUT))
                return -EINVAL;
  
 +      pm_runtime_get_sync(&thdev->dev);
        hubdrv->enable(hub, &thdev->output);
  
        return 0;
@@@ -737,7 -735,6 +770,7 @@@ int intel_th_trace_disable(struct intel
                return -EINVAL;
  
        hubdrv->disable(hub, &thdev->output);
 +      pm_runtime_put(&thdev->dev);
  
        return 0;
  }
@@@ -114,9 -114,6 +114,9 @@@ intel_th_output_assigned(struct intel_t
   * @unassign: deassociate an output type device from an output port
   * @enable:   enable tracing for a given output device
   * @disable:  disable tracing for a given output device
 + * @irq:      interrupt callback
 + * @activate: enable tracing on the output's side
 + * @deactivate:       disable tracing on the output's side
   * @fops:     file operations for device nodes
   * @attr_group:       attributes provided by the driver
   *
@@@ -208,6 -205,9 +208,9 @@@ struct intel_th 
  
        int                     id;
        int                     major;
+ #ifdef CONFIG_MODULES
+       struct work_struct      request_module_work;
+ #endif /* CONFIG_MODULES */
  #ifdef CONFIG_INTEL_TH_DEBUG
        struct dentry           *dbg;
  #endif