summaryrefslogtreecommitdiff
path: root/drivers/s390/cio/chp.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/s390/cio/chp.c')
-rw-r--r--drivers/s390/cio/chp.c141
1 files changed, 81 insertions, 60 deletions
diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c
index 675d7ed82356..a07bbecba61c 100644
--- a/drivers/s390/cio/chp.c
+++ b/drivers/s390/cio/chp.c
@@ -127,10 +127,9 @@ static int s390_vary_chpid(struct chp_id chpid, int on)
/*
* Channel measurement related functions
*/
-static ssize_t chp_measurement_chars_read(struct file *filp,
- struct kobject *kobj,
- struct bin_attribute *bin_attr,
- char *buf, loff_t off, size_t count)
+static ssize_t measurement_chars_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
{
struct channel_path *chp;
struct device *device;
@@ -143,87 +142,79 @@ static ssize_t chp_measurement_chars_read(struct file *filp,
return memory_read_from_buffer(buf, count, &off, &chp->cmg_chars,
sizeof(chp->cmg_chars));
}
+static BIN_ATTR_ADMIN_RO(measurement_chars, sizeof(struct cmg_chars));
-static const struct bin_attribute chp_measurement_chars_attr = {
- .attr = {
- .name = "measurement_chars",
- .mode = S_IRUSR,
- },
- .size = sizeof(struct cmg_chars),
- .read = chp_measurement_chars_read,
-};
-
-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 < 128) {
- area = css->cub_addr1;
- idx = chpid.id;
- } else {
- area = css->cub_addr2;
- idx = chpid.id - 128;
- }
- 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 chp_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;
- size = sizeof(struct cmg_entry);
+ 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 const struct bin_attribute chp_measurement_attr = {
- .attr = {
- .name = "measurement",
- .mode = S_IRUSR,
- },
- .size = sizeof(struct cmg_entry),
- .read = chp_measurement_read,
+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);
void chp_remove_cmg_attr(struct channel_path *chp)
{
- device_remove_bin_file(&chp->dev, &chp_measurement_chars_attr);
- device_remove_bin_file(&chp->dev, &chp_measurement_attr);
+ device_remove_groups(&chp->dev, measurement_groups);
}
int chp_add_cmg_attr(struct channel_path *chp)
{
- int ret;
-
- ret = device_create_bin_file(&chp->dev, &chp_measurement_chars_attr);
- if (ret)
- return ret;
- ret = device_create_bin_file(&chp->dev, &chp_measurement_attr);
- if (ret)
- device_remove_bin_file(&chp->dev, &chp_measurement_chars_attr);
- return ret;
+ return device_add_groups(&chp->dev, measurement_groups);
}
/*
@@ -401,6 +392,35 @@ static ssize_t chp_esc_show(struct device *dev,
}
static DEVICE_ATTR(esc, 0444, chp_esc_show, NULL);
+static char apply_max_suffix(unsigned long *value, unsigned long base)
+{
+ static char suffixes[] = { 0, 'K', 'M', 'G', 'T' };
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(suffixes) - 1; i++) {
+ if (*value < base || *value % base != 0)
+ break;
+ *value /= base;
+ }
+
+ return suffixes[i];
+}
+
+static ssize_t speed_bps_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct channel_path *chp = to_channelpath(dev);
+ unsigned long speed = chp->speed;
+ char suffix;
+
+ suffix = apply_max_suffix(&speed, 1000);
+
+ return suffix ? sysfs_emit(buf, "%lu%c\n", speed, suffix) :
+ sysfs_emit(buf, "%lu\n", speed);
+}
+
+static DEVICE_ATTR_RO(speed_bps);
+
static ssize_t util_string_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
@@ -432,6 +452,7 @@ static struct attribute *chp_attrs[] = {
&dev_attr_chid.attr,
&dev_attr_chid_external.attr,
&dev_attr_esc.attr,
+ &dev_attr_speed_bps.attr,
NULL,
};
static struct attribute_group chp_attr_group = {