s390/cio: export CHPID operating speed
authorPeter Oberparleiter <oberpar@linux.ibm.com>
Tue, 26 Mar 2024 16:03:24 +0000 (17:03 +0100)
committerAlexander Gordeev <agordeev@linux.ibm.com>
Fri, 12 Apr 2024 14:11:29 +0000 (16:11 +0200)
Add a per-CHPID sysfs attribute named "speed_bps" that provides the
operating speed of the associated channel path in bits per second,
or 0 if the operating speed is not available.

Example:

$ cat /sys/devices/css0/chp0.32/speed_bps
32G

Reviewed-by: Vineeth Vijayan <vneethv@linux.ibm.com>
Acked-by: Heiko Carstens <hca@linux.ibm.com>
Signed-off-by: Peter Oberparleiter <oberpar@linux.ibm.com>
Signed-off-by: Alexander Gordeev <agordeev@linux.ibm.com>
drivers/s390/cio/chp.c
drivers/s390/cio/chp.h
drivers/s390/cio/chsc.c

index fd7d34ed6ea9c3588718da82fca4606104fd0af9..a07bbecba61cd491826f67fed8f88a9e1b27520e 100644 (file)
@@ -392,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)
@@ -423,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 = {
index 1241033ccd62bcd71d516c918c445a33a61c2fb8..a15324a43aa3ad50747297d0ad61299b7aadb2a9 100644 (file)
@@ -52,6 +52,7 @@ struct channel_path {
        int cmg;
        int shared;
        int extended;
+       unsigned long speed;
        struct cmg_chars cmg_chars;
 };
 
index bfc663889fbde5580e5865e7ae573189dc3f5625..dcc1e1c34ca2e139367d6eefa7964c87d1432627 100644 (file)
@@ -1066,6 +1066,18 @@ chsc_initialize_cmg_chars(struct channel_path *chp, u8 cmcv,
        }
 }
 
+static unsigned long scmc_get_speed(u32 s, u32 p)
+{
+       unsigned long speed = s;
+
+       if (!p)
+               p = 8;
+       while (p--)
+               speed *= 10;
+
+       return speed;
+}
+
 int chsc_get_channel_measurement_chars(struct channel_path *chp)
 {
        unsigned long flags;
@@ -1086,16 +1098,19 @@ int chsc_get_channel_measurement_chars(struct channel_path *chp)
                u32 : 21;
                u32 chpid : 8;
                u32 cmcv : 5;
-               u32 : 11;
+               u32 : 7;
+               u32 cmgp : 4;
                u32 cmgq : 8;
                u32 cmg : 8;
-               u32 zeroes3;
+               u32 : 16;
+               u32 cmgs : 16;
                u32 data[NR_MEASUREMENT_CHARS];
        } *scmc_area;
 
        chp->shared = -1;
        chp->cmg = -1;
        chp->extended = 0;
+       chp->speed = 0;
 
        if (!css_chsc_characteristics.scmc || !css_chsc_characteristics.secm)
                return -EINVAL;
@@ -1126,6 +1141,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;
+       chp->speed = scmc_get_speed(scmc_area->cmgs, scmc_area->cmgp);
        chsc_initialize_cmg_chars(chp, scmc_area->cmcv,
                                  (struct cmg_chars *) &scmc_area->data);
 out: