浏览代码

Merge tag 'stm-for-greg-20161118' of git://git.kernel.org/pub/scm/linux/kernel/git/ash/stm into char-misc-next

Alexander writes:

stm class/intel_th: Updates for 4.10

These are:
  * Fix for an error-path leak in stm
  * Host-driven mode in intel_th
  * Documentation and other small updates
Greg Kroah-Hartman 8 年之前
父节点
当前提交
38d1790644

+ 22 - 0
Documentation/trace/intel_th.txt

@@ -97,3 +97,25 @@ $ echo 0 > /sys/bus/intel_th/devices/0-msc0/active
 # and now you can collect the trace from the device node:
 # and now you can collect the trace from the device node:
 
 
 $ cat /dev/intel_th0/msc0 > my_stp_trace
 $ cat /dev/intel_th0/msc0 > my_stp_trace
+
+Host Debugger Mode
+==================
+
+It is possible to configure the Trace Hub and control its trace
+capture from a remote debug host, which should be connected via one of
+the hardware debugging interfaces, which will then be used to both
+control Intel Trace Hub and transfer its trace data to the debug host.
+
+The driver needs to be told that such an arrangement is taking place
+so that it does not touch any capture/port configuration and avoids
+conflicting with the debug host's configuration accesses. The only
+activity that the driver will perform in this mode is collecting
+software traces to the Software Trace Hub (an stm class device). The
+user is still responsible for setting up adequate master/channel
+mappings that the decoder on the receiving end would recognize.
+
+In order to enable the host mode, set the 'host_mode' parameter of the
+'intel_th' kernel module to 'y'. None of the virtual output devices
+will show up on the intel_th bus. Also, trace configuration and
+capture controlling attribute groups of the 'gth' device will not be
+exposed. The 'sth' device will operate as usual.

+ 34 - 3
Documentation/trace/stm.txt

@@ -69,12 +69,43 @@ stm device's channel mmio region is 64 bytes and hardware page size is
 width==64, you should be able to mmap() one page on this file
 width==64, you should be able to mmap() one page on this file
 descriptor and obtain direct access to an mmio region for 64 channels.
 descriptor and obtain direct access to an mmio region for 64 channels.
 
 
+Examples of STM devices are Intel(R) Trace Hub [1] and Coresight STM
+[2].
+
+stm_source
+==========
+
 For kernel-based trace sources, there is "stm_source" device
 For kernel-based trace sources, there is "stm_source" device
 class. Devices of this class can be connected and disconnected to/from
 class. Devices of this class can be connected and disconnected to/from
-stm devices at runtime via a sysfs attribute.
+stm devices at runtime via a sysfs attribute called "stm_source_link"
+by writing the name of the desired stm device there, for example:
 
 
-Examples of STM devices are Intel(R) Trace Hub [1] and Coresight STM
-[2].
+$ echo dummy_stm.0 > /sys/class/stm_source/console/stm_source_link
+
+For examples on how to use stm_source interface in the kernel, refer
+to stm_console or stm_heartbeat drivers.
+
+Each stm_source device will need to assume a master and a range of
+channels, depending on how many channels it requires. These are
+allocated for the device according to the policy configuration. If
+there's a node in the root of the policy directory that matches the
+stm_source device's name (for example, "console"), this node will be
+used to allocate master and channel numbers. If there's no such policy
+node, the stm core will pick the first contiguous chunk of channels
+within the first available master. Note that the node must exist
+before the stm_source device is connected to its stm device.
+
+stm_console
+===========
+
+One implementation of this interface also used in the example above is
+the "stm_console" driver, which basically provides a one-way console
+for kernel messages over an stm device.
+
+To configure the master/channel pair that will be assigned to this
+console in the STP stream, create a "console" policy entry (see the
+beginning of this text on how to do that). When initialized, it will
+consume one channel.
 
 
 [1] https://software.intel.com/sites/default/files/managed/d3/3c/intel-th-developer-manual.pdf
 [1] https://software.intel.com/sites/default/files/managed/d3/3c/intel-th-developer-manual.pdf
 [2] http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0444b/index.html
 [2] http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0444b/index.html

+ 19 - 9
drivers/hwtracing/intel_th/core.c

@@ -29,6 +29,9 @@
 #include "intel_th.h"
 #include "intel_th.h"
 #include "debug.h"
 #include "debug.h"
 
 
+static bool host_mode __read_mostly;
+module_param(host_mode, bool, 0444);
+
 static DEFINE_IDA(intel_th_ida);
 static DEFINE_IDA(intel_th_ida);
 
 
 static int intel_th_match(struct device *dev, struct device_driver *driver)
 static int intel_th_match(struct device *dev, struct device_driver *driver)
@@ -380,7 +383,7 @@ static void intel_th_device_free(struct intel_th_device *thdev)
 /*
 /*
  * Intel(R) Trace Hub subdevices
  * Intel(R) Trace Hub subdevices
  */
  */
-static struct intel_th_subdevice {
+static const struct intel_th_subdevice {
 	const char		*name;
 	const char		*name;
 	struct resource		res[3];
 	struct resource		res[3];
 	unsigned		nres;
 	unsigned		nres;
@@ -527,14 +530,19 @@ static int intel_th_populate(struct intel_th *th, struct resource *devres,
 {
 {
 	struct resource res[3];
 	struct resource res[3];
 	unsigned int req = 0;
 	unsigned int req = 0;
-	int i, err;
+	int src, dst, err;
 
 
 	/* create devices for each intel_th_subdevice */
 	/* create devices for each intel_th_subdevice */
-	for (i = 0; i < ARRAY_SIZE(intel_th_subdevices); i++) {
-		struct intel_th_subdevice *subdev = &intel_th_subdevices[i];
+	for (src = 0, dst = 0; src < ARRAY_SIZE(intel_th_subdevices); src++) {
+		const struct intel_th_subdevice *subdev =
+			&intel_th_subdevices[src];
 		struct intel_th_device *thdev;
 		struct intel_th_device *thdev;
 		int r;
 		int r;
 
 
+		/* only allow SOURCE and SWITCH devices in host mode */
+		if (host_mode && subdev->type == INTEL_TH_OUTPUT)
+			continue;
+
 		thdev = intel_th_device_alloc(th, subdev->type, subdev->name,
 		thdev = intel_th_device_alloc(th, subdev->type, subdev->name,
 					      subdev->id);
 					      subdev->id);
 		if (!thdev) {
 		if (!thdev) {
@@ -577,10 +585,12 @@ static int intel_th_populate(struct intel_th *th, struct resource *devres,
 		}
 		}
 
 
 		if (subdev->type == INTEL_TH_OUTPUT) {
 		if (subdev->type == INTEL_TH_OUTPUT) {
-			thdev->dev.devt = MKDEV(th->major, i);
+			thdev->dev.devt = MKDEV(th->major, dst);
 			thdev->output.type = subdev->otype;
 			thdev->output.type = subdev->otype;
 			thdev->output.port = -1;
 			thdev->output.port = -1;
 			thdev->output.scratchpad = subdev->scrpd;
 			thdev->output.scratchpad = subdev->scrpd;
+		} else if (subdev->type == INTEL_TH_SWITCH) {
+			thdev->host_mode = host_mode;
 		}
 		}
 
 
 		err = device_add(&thdev->dev);
 		err = device_add(&thdev->dev);
@@ -597,14 +607,14 @@ static int intel_th_populate(struct intel_th *th, struct resource *devres,
 				req++;
 				req++;
 		}
 		}
 
 
-		th->thdev[i] = thdev;
+		th->thdev[dst++] = thdev;
 	}
 	}
 
 
 	return 0;
 	return 0;
 
 
 kill_subdevs:
 kill_subdevs:
-	for (i-- ; i >= 0; i--)
-		intel_th_device_remove(th->thdev[i]);
+	for (; dst >= 0; dst--)
+		intel_th_device_remove(th->thdev[dst]);
 
 
 	return err;
 	return err;
 }
 }
@@ -717,7 +727,7 @@ void intel_th_free(struct intel_th *th)
 
 
 	intel_th_request_hub_module_flush(th);
 	intel_th_request_hub_module_flush(th);
 	for (i = 0; i < TH_SUBDEVICE_MAX; i++)
 	for (i = 0; i < TH_SUBDEVICE_MAX; i++)
-		if (th->thdev[i] != th->hub)
+		if (th->thdev[i] && th->thdev[i] != th->hub)
 			intel_th_device_remove(th->thdev[i]);
 			intel_th_device_remove(th->thdev[i]);
 
 
 	intel_th_device_remove(th->hub);
 	intel_th_device_remove(th->hub);

+ 24 - 2
drivers/hwtracing/intel_th/gth.c

@@ -564,6 +564,9 @@ static int intel_th_gth_assign(struct intel_th_device *thdev,
 	struct gth_device *gth = dev_get_drvdata(&thdev->dev);
 	struct gth_device *gth = dev_get_drvdata(&thdev->dev);
 	int i, id;
 	int i, id;
 
 
+	if (thdev->host_mode)
+		return -EBUSY;
+
 	if (othdev->type != INTEL_TH_OUTPUT)
 	if (othdev->type != INTEL_TH_OUTPUT)
 		return -EINVAL;
 		return -EINVAL;
 
 
@@ -600,6 +603,9 @@ static void intel_th_gth_unassign(struct intel_th_device *thdev,
 	struct gth_device *gth = dev_get_drvdata(&thdev->dev);
 	struct gth_device *gth = dev_get_drvdata(&thdev->dev);
 	int port = othdev->output.port;
 	int port = othdev->output.port;
 
 
+	if (thdev->host_mode)
+		return;
+
 	spin_lock(&gth->gth_lock);
 	spin_lock(&gth->gth_lock);
 	othdev->output.port = -1;
 	othdev->output.port = -1;
 	othdev->output.active = false;
 	othdev->output.active = false;
@@ -654,9 +660,24 @@ static int intel_th_gth_probe(struct intel_th_device *thdev)
 	gth->base = base;
 	gth->base = base;
 	spin_lock_init(&gth->gth_lock);
 	spin_lock_init(&gth->gth_lock);
 
 
+	/*
+	 * Host mode can be signalled via SW means or via SCRPD_DEBUGGER_IN_USE
+	 * bit. Either way, don't reset HW in this case, and don't export any
+	 * capture configuration attributes. Also, refuse to assign output
+	 * drivers to ports, see intel_th_gth_assign().
+	 */
+	if (thdev->host_mode)
+		goto done;
+
 	ret = intel_th_gth_reset(gth);
 	ret = intel_th_gth_reset(gth);
-	if (ret)
-		return ret;
+	if (ret) {
+		if (ret != -EBUSY)
+			return ret;
+
+		thdev->host_mode = true;
+
+		goto done;
+	}
 
 
 	for (i = 0; i < TH_CONFIGURABLE_MASTERS + 1; i++)
 	for (i = 0; i < TH_CONFIGURABLE_MASTERS + 1; i++)
 		gth->master[i] = -1;
 		gth->master[i] = -1;
@@ -677,6 +698,7 @@ static int intel_th_gth_probe(struct intel_th_device *thdev)
 		return -ENOMEM;
 		return -ENOMEM;
 	}
 	}
 
 
+done:
 	dev_set_drvdata(dev, gth);
 	dev_set_drvdata(dev, gth);
 
 
 	return 0;
 	return 0;

+ 4 - 0
drivers/hwtracing/intel_th/intel_th.h

@@ -54,6 +54,7 @@ struct intel_th_output {
  * @num_resources:	number of resources in @resource array
  * @num_resources:	number of resources in @resource array
  * @type:		INTEL_TH_{SOURCE,OUTPUT,SWITCH}
  * @type:		INTEL_TH_{SOURCE,OUTPUT,SWITCH}
  * @id:			device instance or -1
  * @id:			device instance or -1
+ * @host_mode:		Intel TH is controlled by an external debug host
  * @output:		output descriptor for INTEL_TH_OUTPUT devices
  * @output:		output descriptor for INTEL_TH_OUTPUT devices
  * @name:		device name to match the driver
  * @name:		device name to match the driver
  */
  */
@@ -64,6 +65,9 @@ struct intel_th_device {
 	unsigned int	type;
 	unsigned int	type;
 	int		id;
 	int		id;
 
 
+	/* INTEL_TH_SWITCH specific */
+	bool			host_mode;
+
 	/* INTEL_TH_OUTPUT specific */
 	/* INTEL_TH_OUTPUT specific */
 	struct intel_th_output	output;
 	struct intel_th_output	output;
 
 

+ 5 - 3
drivers/hwtracing/stm/core.c

@@ -361,7 +361,7 @@ static int stm_char_open(struct inode *inode, struct file *file)
 	struct stm_file *stmf;
 	struct stm_file *stmf;
 	struct device *dev;
 	struct device *dev;
 	unsigned int major = imajor(inode);
 	unsigned int major = imajor(inode);
-	int err = -ENODEV;
+	int err = -ENOMEM;
 
 
 	dev = class_find_device(&stm_class, NULL, &major, major_match);
 	dev = class_find_device(&stm_class, NULL, &major, major_match);
 	if (!dev)
 	if (!dev)
@@ -369,8 +369,9 @@ static int stm_char_open(struct inode *inode, struct file *file)
 
 
 	stmf = kzalloc(sizeof(*stmf), GFP_KERNEL);
 	stmf = kzalloc(sizeof(*stmf), GFP_KERNEL);
 	if (!stmf)
 	if (!stmf)
-		return -ENOMEM;
+		goto err_put_device;
 
 
+	err = -ENODEV;
 	stm_output_init(&stmf->output);
 	stm_output_init(&stmf->output);
 	stmf->stm = to_stm_device(dev);
 	stmf->stm = to_stm_device(dev);
 
 
@@ -382,9 +383,10 @@ static int stm_char_open(struct inode *inode, struct file *file)
 	return nonseekable_open(inode, file);
 	return nonseekable_open(inode, file);
 
 
 err_free:
 err_free:
+	kfree(stmf);
+err_put_device:
 	/* matches class_find_device() above */
 	/* matches class_find_device() above */
 	put_device(dev);
 	put_device(dev);
-	kfree(stmf);
 
 
 	return err;
 	return err;
 }
 }