Commit 2dc8903a authored by Peter Oberparleiter's avatar Peter Oberparleiter Committed by Alexander Gordeev
Browse files

s390/cio: export extended channel-path-measurement data



Add a per-CHPID binary sysfs attribute named "ext_measurement" that
provides access to extended channel-path-measurement data for the
associated channel path.

Note that while not all channel-paths provide extended measurement data
this attribute is created unconditionally for all channel paths because
channel-path measurement capabilities might change during run-time.
Reading from the attribute will only return data for channel-paths that
support extended measurement data.

Example:

$ echo 1 > /sys/devices/css0/cm_enable
$ xxd /sys/devices/css0/chp0.32/ext_measurement
00000000: 53e0 8002 0000 0095 0000 0000 59cc e034  S...........Y..4
00000010: 38b8 cc45 0000 0000 0000 0000 3e24 fe94  8..E........>$..
00000020: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000030: 0000 0000 0000 0000 0000 0000 0000 0000  ................

Reviewed-by: default avatarVineeth Vijayan <vneethv@linux.ibm.com>
Tested-by: default avatarVineeth Vijayan <vneethv@linux.ibm.com>
Acked-by: default avatarHeiko Carstens <hca@linux.ibm.com>
Signed-off-by: default avatarPeter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: default avatarAlexander Gordeev <agordeev@linux.ibm.com>
parent b4691baa
Loading
Loading
Loading
Loading
+39 −29
Original line number Diff line number Diff line
@@ -144,55 +144,65 @@ static ssize_t measurement_chars_read(struct file *filp, struct kobject *kobj,
}
static BIN_ATTR_ADMIN_RO(measurement_chars, sizeof(struct cmg_chars));

static void chp_measurement_copy_block(struct cmg_entry *buf,
				       struct channel_subsystem *css,
				       struct chp_id chpid)
{
	void *area;
	struct cmg_entry *entry, reference_buf;
	int idx;

	if (chpid.id < CSS_CUES_PER_PAGE) {
		area = css->cub[0];
		idx = chpid.id;
	} else {
		area = css->cub[1];
		idx = chpid.id - CSS_CUES_PER_PAGE;
	}
	entry = area + (idx * sizeof(struct cmg_entry));
	do {
		memcpy(buf, entry, sizeof(*entry));
		memcpy(&reference_buf, entry, sizeof(*entry));
	} while (reference_buf.values[0] != buf->values[0]);
}

static ssize_t measurement_read(struct file *filp, struct kobject *kobj,
				struct bin_attribute *bin_attr,
				char *buf, loff_t off, size_t count)
static ssize_t chp_measurement_copy_block(void *buf, loff_t off, size_t count,
					  struct kobject *kobj, bool extended)
{
	struct channel_path *chp;
	struct channel_subsystem *css;
	struct device *device;
	unsigned int size;
	void *area, *entry;
	int id, idx;

	device = kobj_to_dev(kobj);
	chp = to_channelpath(device);
	css = to_css(chp->dev.parent);
	id = chp->chpid.id;

	if (extended) {
		/* Check if extended measurement data is available. */
		if (!chp->extended)
			return 0;

		size = sizeof(struct cmg_ext_entry);
		area = css->ecub[id / CSS_ECUES_PER_PAGE];
		idx = id % CSS_ECUES_PER_PAGE;
	} else {
		size = sizeof(struct cmg_entry);
		area = css->cub[id / CSS_CUES_PER_PAGE];
		idx = id % CSS_CUES_PER_PAGE;
	}
	entry = area + (idx * size);

	/* Only allow single reads. */
	if (off || count < size)
		return 0;
	chp_measurement_copy_block((struct cmg_entry *)buf, css, chp->chpid);
	count = size;
	return count;

	memcpy(buf, entry, size);

	return size;
}

static ssize_t measurement_read(struct file *filp, struct kobject *kobj,
				struct bin_attribute *bin_attr,
				char *buf, loff_t off, size_t count)
{
	return chp_measurement_copy_block(buf, off, count, kobj, false);
}
static BIN_ATTR_ADMIN_RO(measurement, sizeof(struct cmg_entry));

static ssize_t ext_measurement_read(struct file *filp, struct kobject *kobj,
				    struct bin_attribute *bin_attr,
				    char *buf, loff_t off, size_t count)
{
	return chp_measurement_copy_block(buf, off, count, kobj, true);
}
static BIN_ATTR_ADMIN_RO(ext_measurement, sizeof(struct cmg_ext_entry));

static struct bin_attribute *measurement_attrs[] = {
	&bin_attr_measurement_chars,
	&bin_attr_measurement,
	&bin_attr_ext_measurement,
	NULL,
};
BIN_ATTRIBUTE_GROUPS(measurement);
+1 −0
Original line number Diff line number Diff line
@@ -51,6 +51,7 @@ struct channel_path {
	/* Channel-measurement related stuff: */
	int cmg;
	int shared;
	int extended;
	struct cmg_chars cmg_chars;
};

+21 −3
Original line number Diff line number Diff line
@@ -871,11 +871,14 @@ int __chsc_do_secm(struct channel_subsystem *css, int enable)
	struct {
		struct chsc_header request;
		u32 operation_code : 2;
		u32 : 30;
		u32 : 1;
		u32 e : 1;
		u32 : 28;
		u32 key : 4;
		u32 : 28;
		dma64_t cub[CSS_NUM_CUB_PAGES];
		u32 reserved[13];
		dma64_t ecub[CSS_NUM_ECUB_PAGES];
		u32 reserved[5];
		struct chsc_header response;
		u32 status : 8;
		u32 : 4;
@@ -892,9 +895,12 @@ int __chsc_do_secm(struct channel_subsystem *css, int enable)
	secm_area->request.code = 0x0016;

	secm_area->key = PAGE_DEFAULT_KEY >> 4;
	secm_area->e = 1;

	for (i = 0; i < CSS_NUM_CUB_PAGES; i++)
		secm_area->cub[i] = (__force dma64_t)virt_to_dma32(css->cub[i]);
	for (i = 0; i < CSS_NUM_ECUB_PAGES; i++)
		secm_area->ecub[i] = virt_to_dma64(css->ecub[i]);

	secm_area->operation_code = enable ? 0 : 1;

@@ -929,6 +935,11 @@ static int cub_alloc(struct channel_subsystem *css)
		if (!css->cub[i])
			return -ENOMEM;
	}
	for (i = 0; i < CSS_NUM_ECUB_PAGES; i++) {
		css->ecub[i] = (void *)get_zeroed_page(GFP_KERNEL);
		if (!css->ecub[i])
			return -ENOMEM;
	}

	return 0;
}
@@ -941,6 +952,10 @@ static void cub_free(struct channel_subsystem *css)
		free_page((unsigned long)css->cub[i]);
		css->cub[i] = NULL;
	}
	for (i = 0; i < CSS_NUM_ECUB_PAGES; i++) {
		free_page((unsigned long)css->ecub[i]);
		css->ecub[i] = NULL;
	}
}

int
@@ -1067,7 +1082,8 @@ int chsc_get_channel_measurement_chars(struct channel_path *chp)
		u32 zeroes2;
		u32 not_valid : 1;
		u32 shared : 1;
		u32 : 22;
		u32 extended : 1;
		u32 : 21;
		u32 chpid : 8;
		u32 cmcv : 5;
		u32 : 11;
@@ -1079,6 +1095,7 @@ int chsc_get_channel_measurement_chars(struct channel_path *chp)

	chp->shared = -1;
	chp->cmg = -1;
	chp->extended = 0;

	if (!css_chsc_characteristics.scmc || !css_chsc_characteristics.secm)
		return -EINVAL;
@@ -1108,6 +1125,7 @@ int chsc_get_channel_measurement_chars(struct channel_path *chp)

	chp->cmg = scmc_area->cmg;
	chp->shared = scmc_area->shared;
	chp->extended = scmc_area->extended;
	if (chp->cmg != 2 && chp->cmg != 3) {
		/* No cmg-dependent data. */
		goto out;
+5 −0
Original line number Diff line number Diff line
@@ -22,6 +22,11 @@ struct cmg_entry {
	u32 values[NR_MEASUREMENT_ENTRIES];
};

#define NR_EXT_MEASUREMENT_ENTRIES 16
struct cmg_ext_entry {
	u32 values[NR_EXT_MEASUREMENT_ENTRIES];
};

struct channel_path_desc_fmt1 {
	u8 flags;
	u8 lsn;
+3 −0
Original line number Diff line number Diff line
@@ -40,6 +40,8 @@

#define CSS_NUM_CUB_PAGES		2
#define CSS_CUES_PER_PAGE		128
#define CSS_NUM_ECUB_PAGES		4
#define CSS_ECUES_PER_PAGE		64

/*
 * Conditions used to specify which subchannels need evaluation
@@ -130,6 +132,7 @@ struct channel_subsystem {
	/* channel measurement related */
	int cm_enabled;
	void *cub[CSS_NUM_CUB_PAGES];
	void *ecub[CSS_NUM_ECUB_PAGES];
	/* for orphaned ccw devices */
	struct subchannel *pseudo_subchannel;
};