Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 80f18410 authored by Chris Metcalf's avatar Chris Metcalf
Browse files

tile: support reporting Tilera hypervisor statistics



Newer hypervisors have an API for reporting per-cpu statistics
information.  This change allows seeing that information via
/sys/devices/system/cpu/cpuN/hv_stats file for each core.

Signed-off-by: default avatarChris Metcalf <cmetcalf@tilera.com>
parent 8157107b
Loading
Loading
Loading
Loading
+13 −3
Original line number Diff line number Diff line
@@ -544,14 +544,24 @@ typedef enum {
  HV_CONFSTR_CPUMOD_REV      = 18,

  /** Human-readable CPU module description. */
  HV_CONFSTR_CPUMOD_DESC     = 19
  HV_CONFSTR_CPUMOD_DESC     = 19,

  /** Per-tile hypervisor statistics.  When this identifier is specified,
   *  the hv_confstr call takes two extra arguments.  The first is the
   *  HV_XY_TO_LOTAR of the target tile's coordinates.  The second is
   *  a flag word.  The only current flag is the lowest bit, which means
   *  "zero out the stats instead of retrieving them"; in this case the
   *  buffer and buffer length are ignored. */
  HV_CONFSTR_HV_STATS        = 20

} HV_ConfstrQuery;

/** Query a configuration string from the hypervisor.
 *
 * @param query Identifier for the specific string to be retrieved
 *        (HV_CONFSTR_xxx).
 *        (HV_CONFSTR_xxx).  Some strings may require or permit extra
 *        arguments to be appended which select specific objects to be
 *        described; see the string descriptions above.
 * @param buf Buffer in which to place the string.
 * @param len Length of the buffer.
 * @return If query is valid, then the length of the corresponding string,
@@ -559,7 +569,7 @@ typedef enum {
 *        was truncated.  If query is invalid, HV_EINVAL.  If the specified
 *        buffer is not writable by the client, HV_EFAULT.
 */
int hv_confstr(HV_ConfstrQuery query, HV_VirtAddr buf, int len);
int hv_confstr(HV_ConfstrQuery query, HV_VirtAddr buf, int len, ...);

/** Tile coordinate */
typedef struct
+76 −0
Original line number Diff line number Diff line
@@ -157,6 +157,67 @@ hvconfig_bin_read(struct file *filp, struct kobject *kobj,
	return count;
}

static ssize_t hv_stats_show(struct device *dev,
			     struct device_attribute *attr,
			     char *page)
{
	int cpu = dev->id;
	long lotar = HV_XY_TO_LOTAR(cpu_x(cpu), cpu_y(cpu));

	ssize_t n = hv_confstr(HV_CONFSTR_HV_STATS,
			       (unsigned long)page, PAGE_SIZE - 1,
			       lotar, 0);
	n = n < 0 ? 0 : min(n, (ssize_t)PAGE_SIZE - 1);
	page[n] = '\0';
	return n;
}

static ssize_t hv_stats_store(struct device *dev,
			      struct device_attribute *attr,
			      const char *page,
			      size_t count)
{
	int cpu = dev->id;
	long lotar = HV_XY_TO_LOTAR(cpu_x(cpu), cpu_y(cpu));

	ssize_t n = hv_confstr(HV_CONFSTR_HV_STATS, 0, 0, lotar, 1);
	return n < 0 ? n : count;
}

static DEVICE_ATTR(hv_stats, 0644, hv_stats_show, hv_stats_store);

static int hv_stats_device_add(struct device *dev, struct subsys_interface *sif)
{
	int err, cpu = dev->id;

	if (!cpu_online(cpu))
		return 0;

	err = sysfs_create_file(&dev->kobj, &dev_attr_hv_stats.attr);

	return err;
}

static int hv_stats_device_remove(struct device *dev,
				  struct subsys_interface *sif)
{
	int cpu = dev->id;

	if (!cpu_online(cpu))
		return 0;

	sysfs_remove_file(&dev->kobj, &dev_attr_hv_stats.attr);
	return 0;
}


static struct subsys_interface hv_stats_interface = {
	.name			= "hv_stats",
	.subsys			= &cpu_subsys,
	.add_dev		= hv_stats_device_add,
	.remove_dev		= hv_stats_device_remove,
};

static int __init create_sysfs_entries(void)
{
	int err = 0;
@@ -188,6 +249,21 @@ static int __init create_sysfs_entries(void)
		err = sysfs_create_bin_file(hypervisor_kobj, &hvconfig_bin);
	}

	if (!err) {
		/*
		 * Don't bother adding the hv_stats files on each CPU if
		 * our hypervisor doesn't supply statistics.
		 */
		int cpu = raw_smp_processor_id();
		long lotar = HV_XY_TO_LOTAR(cpu_x(cpu), cpu_y(cpu));
		char dummy;
		ssize_t n = hv_confstr(HV_CONFSTR_HV_STATS,
				       (unsigned long) &dummy, 1,
				       lotar, 0);
		if (n >= 0)
			err = subsys_interface_register(&hv_stats_interface);
	}

	return err;
}
subsys_initcall(create_sysfs_entries);